| | |
| | | package com.rongyichuang.dashboard.service; |
| | | |
| | | import com.rongyichuang.activity.repository.ActivityRepository; |
| | | import com.rongyichuang.dashboard.dto.response.DashboardStatsResponse; |
| | | import com.rongyichuang.dashboard.dto.response.RegionRegistrationStat; |
| | | import com.rongyichuang.dashboard.dto.response.RegistrationTrendPoint; |
| | | import com.rongyichuang.judge.repository.JudgeRepository; |
| | | import com.rongyichuang.player.repository.ActivityPlayerRepository; |
| | | import com.rongyichuang.player.repository.PlayerRepository; |
| | | import com.rongyichuang.dashboard.dto.response.DashboardStatsResponse; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * Dashboard 统计数据服务 |
| | | */ |
| | | @Service |
| | | public class DashboardService { |
| | | |
| | | private static final int DEFAULT_TREND_DAYS = 15; |
| | | private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | private static final String UNASSIGNED_REGION_NAME = "未选择区域"; |
| | | |
| | | @Autowired |
| | | private ActivityRepository activityRepository; |
| | |
| | | long activeActivities = activityRepository.countActiveActivities(); |
| | | stats.setActiveActivities((int) activeActivities); |
| | | |
| | | // 参赛总人数(状态为1的选手) |
| | | // 参赛总人数(状态为1的参赛选手) |
| | | long totalPlayers = playerRepository.countByState(1); |
| | | stats.setTotalPlayers((int) totalPlayers); |
| | | |
| | |
| | | |
| | | return stats; |
| | | } |
| | | |
| | | /** |
| | | * 获取最近报名趋势(默认最近15天,仅统计第一阶段) |
| | | */ |
| | | public List<RegistrationTrendPoint> getRegistrationTrend(Integer days) { |
| | | int queryDays = (days == null || days <= 0) ? DEFAULT_TREND_DAYS : days; |
| | | LocalDate today = LocalDate.now(); |
| | | LocalDate startDate = today.minusDays(queryDays - 1L); |
| | | LocalDateTime startDateTime = startDate.atStartOfDay(); |
| | | |
| | | List<Object[]> rawData = activityPlayerRepository.countFirstStageRegistrationsByDate(startDateTime); |
| | | Map<String, Long> dataMap = new HashMap<>(); |
| | | |
| | | for (Object[] row : rawData) { |
| | | if (row == null || row.length < 2) { |
| | | continue; |
| | | } |
| | | Object dateObj = row[0]; |
| | | LocalDate date; |
| | | if (dateObj instanceof java.sql.Date sqlDate) { |
| | | date = sqlDate.toLocalDate(); |
| | | } else if (dateObj instanceof LocalDate localDate) { |
| | | date = localDate; |
| | | } else { |
| | | date = LocalDate.parse(dateObj.toString()); |
| | | } |
| | | Long count = row[1] != null ? ((Number) row[1]).longValue() : 0L; |
| | | dataMap.put(date.format(DATE_FORMATTER), count); |
| | | } |
| | | |
| | | List<RegistrationTrendPoint> result = new ArrayList<>(queryDays); |
| | | for (int i = 0; i < queryDays; i++) { |
| | | LocalDate date = startDate.plusDays(i); |
| | | String key = date.format(DATE_FORMATTER); |
| | | Long count = dataMap.getOrDefault(key, 0L); |
| | | result.add(new RegistrationTrendPoint(key, count)); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 获取区域报名分布(仅统计第一阶段,剔除非叶子区域) |
| | | */ |
| | | public List<RegionRegistrationStat> getRegionRegistrationStats() { |
| | | List<Object[]> rawData = activityPlayerRepository.countFirstStageRegistrationsByRegion(); |
| | | List<RegionRegistrationStat> stats = new ArrayList<>(); |
| | | |
| | | for (Object[] row : rawData) { |
| | | if (row == null || row.length < 4) { |
| | | continue; |
| | | } |
| | | String regionId = null; |
| | | if (row[0] != null) { |
| | | Object regionIdObj = row[0]; |
| | | if (regionIdObj instanceof Number number) { |
| | | regionId = String.valueOf(number.longValue()); |
| | | } else { |
| | | regionId = regionIdObj.toString(); |
| | | } |
| | | } |
| | | String regionName = row[1] != null ? row[1].toString() : UNASSIGNED_REGION_NAME; |
| | | // leaf_flag字段在不同驱动下的类型可能差异,需要统一转换 |
| | | Boolean leafFlag = null; |
| | | Object leafObj = row[2]; |
| | | if (leafObj instanceof Number number) { |
| | | leafFlag = number.intValue() == 1; |
| | | } else if (leafObj instanceof Boolean bool) { |
| | | leafFlag = bool; |
| | | } else if (leafObj != null) { |
| | | String leafText = leafObj.toString().trim().toLowerCase(); |
| | | if (!leafText.isEmpty()) { |
| | | if ("1".equals(leafText) || "true".equals(leafText)) { |
| | | leafFlag = true; |
| | | } else if ("0".equals(leafText) || "false".equals(leafText)) { |
| | | leafFlag = false; |
| | | } |
| | | } |
| | | } |
| | | Long count = row[3] != null ? ((Number) row[3]).longValue() : 0L; |
| | | |
| | | if (Boolean.FALSE.equals(leafFlag)) { |
| | | // 非叶子区域不参与统计 |
| | | continue; |
| | | } |
| | | |
| | | if (regionName == null || regionName.isBlank()) { |
| | | regionName = UNASSIGNED_REGION_NAME; |
| | | } |
| | | |
| | | stats.add(new RegionRegistrationStat(regionId, regionName, leafFlag, count)); |
| | | } |
| | | |
| | | return stats; |
| | | } |
| | | } |