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/judge/service/JudgeService.java |  198 +++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 174 insertions(+), 24 deletions(-)

diff --git a/backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java b/backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java
index 63eef94..c4d82ec 100644
--- a/backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java
+++ b/backend/src/main/java/com/rongyichuang/judge/service/JudgeService.java
@@ -2,23 +2,33 @@
 
 import com.rongyichuang.judge.dto.request.JudgeInput;
 import com.rongyichuang.judge.dto.response.JudgeResponse;
+import com.rongyichuang.judge.dto.response.JudgeStatsResponse;
 import com.rongyichuang.judge.entity.Judge;
-import com.rongyichuang.judge.entity.Tag;
+import com.rongyichuang.tag.entity.Tag;
 import com.rongyichuang.judge.repository.JudgeRepository;
 import com.rongyichuang.common.entity.Media;
 import com.rongyichuang.common.repository.MediaRepository;
 import com.rongyichuang.common.dto.request.MediaInput;
 import com.rongyichuang.common.dto.response.MediaResponse;
 import com.rongyichuang.common.service.MediaService;
-import com.rongyichuang.judge.repository.TagRepository;
+import com.rongyichuang.common.exception.BusinessException;
+import com.rongyichuang.common.enums.MediaTargetType;
+import com.rongyichuang.common.util.UserContextUtil;
+import com.rongyichuang.tag.repository.TagRepository;
+import com.rongyichuang.user.service.UserService;
+import com.rongyichuang.user.entity.User;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Sort;
+import org.springframework.jdbc.core.JdbcTemplate;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -31,13 +41,24 @@
     private final TagRepository tagRepository;
     private final MediaRepository mediaRepository;
     private final MediaService mediaService;
+    private final UserService userService;
+    private final JdbcTemplate jdbcTemplate;
+    private final UserContextUtil userContextUtil;
+    
+    @Value("${app.media-url}")
+    private String mediaBaseUrl;
 
     public JudgeService(JudgeRepository judgeRepository, TagRepository tagRepository, 
-                       MediaRepository mediaRepository, MediaService mediaService) {
+                       MediaRepository mediaRepository, MediaService mediaService,
+                       UserService userService, JdbcTemplate jdbcTemplate,
+                       UserContextUtil userContextUtil) {
         this.judgeRepository = judgeRepository;
         this.tagRepository = tagRepository;
         this.mediaRepository = mediaRepository;
         this.mediaService = mediaService;
+        this.userService = userService;
+        this.jdbcTemplate = jdbcTemplate;
+        this.userContextUtil = userContextUtil;
     }
 
     public List<JudgeResponse> findAll() {
@@ -58,17 +79,93 @@
                 .orElse(null);
     }
 
+    /**
+     * 鏍规嵁鐢ㄦ埛ID鑾峰彇璇勫淇℃伅
+     */
+    public Judge findByUserId(Long userId) {
+        Optional<Judge> judge = judgeRepository.findByUserId(userId);
+        return judge.orElse(null);
+    }
+
+    /**
+     * 鑾峰彇褰撳墠璇勫鐨勭粺璁℃暟鎹�
+     */
+    public JudgeStatsResponse getJudgeStats() {
+        log.info("鑾峰彇璇勫缁熻鏁版嵁");
+        
+        Long currentJudgeId = userContextUtil.getCurrentJudgeId();
+        if (currentJudgeId == null) {
+            throw new RuntimeException("褰撳墠鐢ㄦ埛涓嶆槸璇勫锛屾棤娉曟煡璇㈣瘎瀹$粺璁�");
+        }
+        
+        // 鏌ヨ鎴戞湭璇勫鐨勬暟閲�
+        String unReviewedSql = "SELECT COUNT(*) FROM t_activity_player ap " +
+                              "WHERE EXISTS (" +
+                              "  SELECT 1 FROM t_activity_judge aj " +
+                              "  WHERE ap.activity_id = aj.activity_id " +
+                              "  AND ap.stage_id = aj.stage_id " +
+                              "  AND aj.judge_id = ? " +
+                              ") " +
+                              "AND NOT EXISTS (" +
+                              "  SELECT 1 FROM t_activity_player_rating apr " +
+                              "  WHERE ap.id = apr.activity_player_id " +
+                              "  AND apr.judge_id = ? " +
+                              "  AND apr.state = 1 " +
+                              ") " +
+                              "AND ap.state = 1";
+        Integer unReviewedCount = jdbcTemplate.queryForObject(unReviewedSql, Integer.class, currentJudgeId, currentJudgeId);
+
+        // 鏌ヨ鎴戝凡璇勫鐨勬暟閲�
+        String reviewedSql = "SELECT COUNT(*) FROM t_activity_player ap " +
+                            "WHERE EXISTS (" +
+                            "  SELECT 1 FROM t_activity_judge aj " +
+                            "  WHERE ap.activity_id = aj.activity_id " +
+                            "  AND ap.stage_id = aj.stage_id " +
+                            "  AND aj.judge_id = ? " +
+                            ") " +
+                            "AND EXISTS (" +
+                            "  SELECT 1 FROM t_activity_player_rating apr " +
+                            "  WHERE ap.id = apr.activity_player_id " +
+                            "  AND apr.judge_id = ? " +
+                            "  AND apr.state = 1 " +
+                            ") " +
+                            "AND ap.state = 1";
+        Integer reviewedCount = jdbcTemplate.queryForObject(reviewedSql, Integer.class, currentJudgeId, currentJudgeId);
+
+        // 璁$畻鎬绘暟
+        Integer totalCount = (unReviewedCount != null ? unReviewedCount : 0) + (reviewedCount != null ? reviewedCount : 0);
+
+        log.info("璇勫缁熻鏁版嵁 - 寰呰瘎瀹�: {}, 宸茶瘎瀹�: {}, 鎬绘暟: {}", unReviewedCount, reviewedCount, totalCount);
+
+        return new JudgeStatsResponse(
+            unReviewedCount != null ? unReviewedCount : 0,
+            reviewedCount != null ? reviewedCount : 0,
+            totalCount
+        );
+    }
+
     @Transactional
     public JudgeResponse save(JudgeInput input) {
         Judge judge;
+        User user;
+        
+        // 澶勭悊User琛ㄩ�昏緫
+        if (input.getPassword() != null && !input.getPassword().trim().isEmpty() && !"鈥⑩�⑩�⑩�⑩�⑩�⑩�⑩��".equals(input.getPassword())) {
+            // 鏈夊瘑鐮佷笖涓嶆槸鍗犱綅绗︽椂锛屽垱寤烘垨鏇存柊鐢ㄦ埛锛堝寘鍚瘑鐮侊級
+            user = userService.findOrCreateUserByPhone(input.getPhone(), input.getName(), input.getPassword());
+        } else {
+            // 鏃犲瘑鐮佹垨鏄崰浣嶇鏃讹紝鍙洿鏂扮敤鎴峰熀鏈俊鎭紙涓嶆洿鏂板瘑鐮侊級
+            user = userService.findOrCreateUserByPhone(input.getPhone(), input.getName(), null);
+        }
+
         if (input.getId() != null) {
             judge = judgeRepository.findById(input.getId())
-                    .orElseThrow(() -> new RuntimeException("璇勫涓嶅瓨鍦�"));
+                    .orElseThrow(() -> new BusinessException("璇勫涓嶅瓨鍦�"));
         } else {
             judge = new Judge();
             // 鏂板璇勫鏃舵鏌ユ墜鏈哄彿鏄惁宸插瓨鍦�
             if (judgeRepository.existsByPhone(input.getPhone())) {
-                throw new RuntimeException("鎵嬫満鍙风爜宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠栨墜鏈哄彿鐮�");
+                throw new BusinessException("PHONE_EXISTS", "鎵嬫満鍙风爜宸插瓨鍦紝璇蜂娇鐢ㄥ叾浠栨墜鏈哄彿鐮�");
             }
         }
 
@@ -76,16 +173,18 @@
         judge.setPhone(input.getPhone());
         judge.setGender(input.getGender());
         judge.setDescription(input.getDescription());
-
-        // 澶村儚澶勭悊宸茬Щ闄わ紝鍥犱负鏁版嵁搴撹〃缁撴瀯涓病鏈塧vatar_media_id瀛楁
+        judge.setTitle(input.getTitle());
+        judge.setCompany(input.getCompany());
+        judge.setIntroduction(input.getIntroduction());
+        judge.setUserId(user.getId()); // 璁剧疆鍏宠仈鐨勭敤鎴稩D
         // 澶村儚淇℃伅閫氳繃t_media琛ㄧ殑target_type鍜宼arget_id鍏宠仈
 
         // 璁剧疆涓撲笟鏍囩
         if (input.getMajorIds() != null && !input.getMajorIds().isEmpty()) {
-            List<Tag> specialties = tagRepository.findAllById(input.getMajorIds());
-            judge.setSpecialties(specialties);
+            List<Tag> specialtiesList = tagRepository.findAllById(input.getMajorIds());
+            judge.setSpecialties(new HashSet<>(specialtiesList));
         } else {
-            judge.setSpecialties(Collections.emptyList());
+            judge.setSpecialties(Collections.emptySet());
         }
 
         Judge savedJudge = judgeRepository.save(judge);
@@ -95,7 +194,7 @@
     @Transactional
     public void delete(Long id) {
         Judge judge = judgeRepository.findById(id)
-                .orElseThrow(() -> new RuntimeException("璇勫涓嶅瓨鍦�"));
+                .orElseThrow(() -> new BusinessException("璇勫涓嶅瓨鍦�"));
         judgeRepository.delete(judge);
     }
 
@@ -108,16 +207,21 @@
         response.setPhone(judge.getPhone());
         response.setGender(judge.getGender());
         response.setDescription(judge.getDescription());
+        response.setTitle(judge.getTitle());
+        response.setCompany(judge.getCompany());
+        response.setIntroduction(judge.getIntroduction());
         
-        // 鏌ヨ澶村儚淇℃伅锛歵arget_type=1琛ㄧず璇勫锛宼arget_id涓鸿瘎濮擨D
+        // 鏌ヨ澶村儚淇℃伅锛氫娇鐢ㄦ灇涓惧父閲忚〃绀鸿瘎濮斿ご鍍忕被鍨�
         log.info("=== Querying media for judge ID: {}", judge.getId());
-        List<Media> avatarMedias = mediaRepository.findByTargetTypeAndTargetIdAndState(1, judge.getId(), 1);
+        List<Media> avatarMedias = mediaRepository.findByTargetTypeAndTargetIdAndState(
+            MediaTargetType.JUDGE_AVATAR.getValue(), judge.getId(), 1);
         log.info("=== Found {} media records", avatarMedias.size());
         
         if (!avatarMedias.isEmpty()) {
             // 鍙栫涓�涓獟浣撲綔涓哄ご鍍�
             Media avatarMedia = avatarMedias.get(0);
-            String avatarUrl = avatarMedia.getPath();
+            String avatarPath = avatarMedia.getPath();
+            String avatarUrl = buildFullMediaUrl(avatarPath);
             log.info("=== Setting avatarUrl: {}", avatarUrl);
             response.setAvatarUrl(avatarUrl);
         } else {
@@ -138,20 +242,63 @@
 
         return response;
     }
+    
+    /**
+     * 鏋勫缓瀹屾暣鐨勫獟浣揢RL
+     * @param path 濯掍綋璺緞
+     * @return 瀹屾暣鐨刄RL
+     */
+    private String buildFullMediaUrl(String path) {
+        if (!StringUtils.hasText(path)) {
+            return null;
+        }
+        
+        // 濡傛灉璺緞宸茬粡鏄畬鏁碪RL锛岀洿鎺ヨ繑鍥�
+        if (path.startsWith("http://") || path.startsWith("https://")) {
+            return path;
+        }
+        
+        // 鏋勫缓瀹屾暣URL
+        if (StringUtils.hasText(mediaBaseUrl)) {
+            // 纭繚baseUrl浠�/缁撳熬锛宲ath涓嶄互/寮�澶�
+            String baseUrl = mediaBaseUrl.endsWith("/") ? mediaBaseUrl : mediaBaseUrl + "/";
+            String relativePath = path.startsWith("/") ? path.substring(1) : path;
+            return baseUrl + relativePath;
+        }
+        
+        // 濡傛灉娌℃湁閰嶇疆baseUrl锛岃繑鍥炲師璺緞
+        return path;
+    }
 
     /**
      * 淇濆瓨濯掍綋淇℃伅
      */
     public MediaResponse saveMediaInfo(MediaInput input) {
-        Media media = mediaService.saveMedia(
-            input.getName(),
-            input.getPath(),
-            input.getFileSize(),
-            input.getFileExt(),
-            input.getMediaType(),
-            input.getTargetType(),
-            input.getTargetId()
-        );
+        Media media;
+        
+        // 濡傛灉鏈夌缉鐣ュ浘璺緞锛屼娇鐢ㄦ敮鎸佺缉鐣ュ浘鐨勬柟娉�
+        if (input.getThumbPath() != null && !input.getThumbPath().trim().isEmpty()) {
+            media = mediaService.saveMedia(
+                input.getName(),
+                input.getPath(),
+                input.getFileSize(),
+                input.getFileExt(),
+                input.getMediaType(),
+                input.getTargetType(),
+                input.getTargetId(),
+                input.getThumbPath()
+            );
+        } else {
+            media = mediaService.saveMedia(
+                input.getName(),
+                input.getPath(),
+                input.getFileSize(),
+                input.getFileExt(),
+                input.getMediaType(),
+                input.getTargetType(),
+                input.getTargetId()
+            );
+        }
         
         MediaResponse response = new MediaResponse();
         response.setId(media.getId());
@@ -162,6 +309,9 @@
         response.setMediaType(media.getMediaType());
         response.setTargetType(media.getTargetType());
         response.setTargetId(media.getTargetId());
+        response.setThumbPath(media.getThumbPath());
+        response.setDuration(media.getDuration());
+        response.setDescription(media.getDescription());
         
         return response;
     }

--
Gitblit v1.8.0