From ba94ceae1315174798ae1967ef62268c6d16cd5b Mon Sep 17 00:00:00 2001
From: Codex Assistant <codex@example.com>
Date: 星期一, 06 十月 2025 22:07:06 +0800
Subject: [PATCH] feat: 评审与活动相关改动 - backend(GraphQL): Activity schema 增加 updateActivityState(id, state);实现 resolver/service 仅更新 state=2 作为逻辑删除 - backend(GraphQL): region.graphqls 新增 Query leafRegions - backend(GraphQL): player.graphqls 的 projectReviewApplications 增加可选参数 regionId - backend(Service): listProjectReviewApplications 绑定 regionId 参数,修复 QueryParameterException - frontend(web): 新增 api/activity.js 的 updateActivityState 并接入 activity-list 删除逻辑 - frontend(web): review-list.vue 权限仅校验登录,移除角色限制;查询参数修正为 name/regionId - frontend(web): 删除未引用的 ActivityList.vue - frontend(web): projectReviewNew.js GraphQL 查询增加 name 参数

---
 backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java |  130 +++++++++++++++++++++++++++++++++++++++---
 1 files changed, 119 insertions(+), 11 deletions(-)

diff --git a/backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java b/backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java
index b97fdf0..ae96164 100644
--- a/backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java
+++ b/backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java
@@ -1,12 +1,22 @@
 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 缁熻鏁版嵁鏈嶅姟
@@ -14,15 +24,19 @@
 @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;
-    
+
     @Autowired
     private PlayerRepository playerRepository;
-    
+
     @Autowired
     private ActivityPlayerRepository activityPlayerRepository;
-    
+
     @Autowired
     private JudgeRepository judgeRepository;
 
@@ -31,23 +45,117 @@
      */
     public DashboardStatsResponse getDashboardStats() {
         DashboardStatsResponse stats = new DashboardStatsResponse();
-        
+
         // 褰撳墠杩涜姣旇禌鏁伴噺锛堢姸鎬佷负1鐨勬瘮璧涳紝pid=0琛ㄧず涓绘瘮璧涳級
         long activeActivities = activityRepository.countActiveActivities();
         stats.setActiveActivities((int) activeActivities);
-        
-        // 鍙傝禌鎬讳汉鏁帮紙鐘舵�佷负1鐨勯�夋墜锛�
+
+        // 鍙傝禌鎬讳汉鏁帮紙鐘舵�佷负1鐨勫弬璧涢�夋墜锛�
         long totalPlayers = playerRepository.countByState(1);
         stats.setTotalPlayers((int) totalPlayers);
-        
+
         // 鎶ュ悕寰呭鏍镐汉鏁帮紙activityPlayer鐘舵�佷负0琛ㄧず寰呭鏍革級
         long pendingReviews = activityPlayerRepository.countByState(0);
         stats.setPendingReviews((int) pendingReviews);
-        
+
         // 璇勫鎬绘暟锛堢姸鎬佷负1鐨勮瘎濮旓級
         long totalJudges = judgeRepository.countByState(1);
         stats.setTotalJudges((int) totalJudges);
-        
+
         return stats;
     }
-}
\ No newline at end of file
+
+    /**
+     * 鑾峰彇鏈�杩戞姤鍚嶈秼鍔匡紙榛樿鏈�杩�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;
+    }
+}

--
Gitblit v1.8.0