From 93eb6b470773bc49ea6e1a9d4cbd914eb95d525b Mon Sep 17 00:00:00 2001
From: lrj <owen.stl@gmail.com>
Date: 星期二, 30 九月 2025 17:38:04 +0800
Subject: [PATCH] feat: 完善比赛晋级功能并清理测试文件

---
 web/src/views/project-review/detail.vue                                                           |  530 ++
 backend/src/main/java/com/rongyichuang/media/service/MediaV2Service.java                          |   58 
 web/src/api/player.js                                                                             |  121 
 backend/src/main/java/com/rongyichuang/activity/entity/Activity.java                              |   17 
 backend/src/main/java/com/rongyichuang/media/dto/MediaSaveInput.java                              |   20 
 backend/src/test/java/com/rongyichuang/CreateJudgeForUser2Test.java                               |  107 
 backend/src/main/java/com/rongyichuang/common/enums/MediaTargetType.java                          |    5 
 web/src/components/SubmissionFiles.vue                                                            |  130 
 backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java            |   12 
 backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantResponse.java              |  113 
 web/src/api/graphql.ts                                                                            |    2 
 backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantsResponse.java             |   71 
 web/src/views/project-review/index.vue                                                            |  317 +
 backend/src/main/resources/graphql/rating.graphqls                                                |    5 
 web/src/views/test/graphql-test.vue                                                               |  192 
 wx/pages/registration/registration.wxss                                                           |    6 
 backend/src/test/java/com/rongyichuang/CheckUserPermissionTest.java                               |   97 
 web/src/api/region.js                                                                             |  255 
 web/src/router/index.ts                                                                           |   44 
 web/src/api/activityPlayer.js                                                                     |  192 
 backend/src/main/java/com/rongyichuang/player/dto/response/SubmissionMediaResponse.java           |    4 
 backend/src/main/resources/graphql/activity.graphqls                                              |    2 
 backend/src/test/java/com/rongyichuang/CheckMediaRecordsTest.java                                 |  121 
 web/src/api/judge.ts                                                                              |   28 
 backend/src/main/java/com/rongyichuang/message/entity/Message.java                                |  154 
 web/src/layout/index.vue                                                                          |    4 
 web/src/views/player/detail.vue                                                                   |  774 ++++
 web/src/api/activity.js                                                                           |  103 
 backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java                   |   86 
 backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerDetailResponse.java      |   12 
 backend/src/main/java/com/rongyichuang/common/api/DataCleanupController.java                      |   40 
 backend/src/test/java/com/rongyichuang/DatabaseSchemaTest.java                                    |   16 
 backend/src/main/resources/graphql/player.graphqls                                                |  100 
 web/src/views/player/index.vue                                                                    |  115 
 backend/src/main/java/com/rongyichuang/activity/dto/ActivityResponse.java                         |   10 
 wx/lib/cosUtil-example.md                                                                         |    2 
 wx/lib/cosUtil.js                                                                                 |   94 
 web/src/api/rating.js                                                                             |  227 
 腾讯云COS文档预览方案调研.md                                                                                 |  107 
 web/src/api/media.js                                                                              |   74 
 backend/src/main/java/com/rongyichuang/activity/entity/ActivityStatus.java                        |   17 
 backend/src/test/java/com/rongyichuang/AssignActivityPermissionTest.java                          |   96 
 backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java                           |  134 
 backend/src/main/java/com/rongyichuang/player/dto/PromotionInput.java                             |   47 
 backend/src/main/java/com/rongyichuang/player/dto/response/JudgeRatingStatusResponse.java         |   44 
 backend/src/main/java/com/rongyichuang/player/service/PromotionService.java                       |  328 +
 web/src/views/ActivityForm.vue                                                                    |  248 
 web/src/views/region/index.vue                                                                    |    1 
 db.sql                                                                                            |  210 
 web/src/views/review/detail.vue                                                                   |  566 ++
 backend/src/main/java/com/rongyichuang/common/util/UserContextUtil.java                           |   15 
 backend/src/main/java/com/rongyichuang/message/repository/MessageRepository.java                  |   53 
 backend/src/main/java/com/rongyichuang/player/dto/CompetitionParticipantResponse.java             |  108 
 backend/src/test/java/com/rongyichuang/SimpleUserCheckTest.java                                   |   79 
 backend/src/test/java/com/rongyichuang/CheckJudgeTableTest.java                                   |   50 
 web/src/config/api.ts                                                                             |   69 
 backend/src/main/java/com/rongyichuang/activity/dto/ActivityInput.java                            |    9 
 clear_tables.sql                                                                                  |   42 
 web/src/api/carousel.js                                                                           |   28 
 backend/src/main/java/com/rongyichuang/player/dto/response/PlayerInfoResponse.java                |   24 
 web/src/utils/appConfig.js                                                                        |    2 
 backend/src/main/java/com/rongyichuang/player/entity/ActivityPlayer.java                          |    5 
 backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java            |   21 
 backend/src/main/java/com/rongyichuang/rating/dto/response/RatingSchemeResponse.java              |   32 
 wx/pages/registration/registration.js                                                             |  230 +
 backend/src/test/java/com/rongyichuang/CheckActivityJudgeTableTest.java                           |   50 
 backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java                |  112 
 web/src/api/config.js                                                                             |   52 
 backend/src/main/java/com/rongyichuang/rating/service/RatingSchemeService.java                    |    2 
 backend/db.sql                                                                                    |    2 
 backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java               |   26 
 web/src/views/competition-promotion/index.vue                                                     |  727 +++
 backend/src/main/java/com/rongyichuang/media/api/MediaV2GraphqlApi.java                           |   25 
 web/src/utils/graphql.ts                                                                          |    2 
 web/src/api/dashboard.js                                                                          |   20 
 web/src/views/review/index.vue                                                                    |  290 +
 backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java |    4 
 web/src/api/projectReview.js                                                                      |  279 +
 web/src/constants/mediaTargetType.ts                                                              |    4 
 backend/src/main/java/com/rongyichuang/player/dto/PromotionResult.java                            |   53 
 backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java            |  185 
 backend/src/main/java/com/rongyichuang/config/SecurityConfig.java                                 |   20 
 backend/src/main/java/com/rongyichuang/rating/entity/RatingItem.java                              |    8 
 backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerService.java                  |  257 +
 backend/src/main/java/com/rongyichuang/activity/entity/ActivityPlayerRating.java                  |   19 
 t_media.sql                                                                                       |   57 
 backend/src/main/java/com/rongyichuang/player/dto/PromotionCompetitionResponse.java               |  123 
 backend/src/main/java/com/rongyichuang/message/entity/MessageType.java                            |   49 
 backend/src/main/resources/graphql/media.graphqls                                                 |    8 
 web/src/components/PlayerInfoCard.vue                                                             |   40 
 web/src/views/ActivityDetail.vue                                                                  |   48 
 页面bug.png                                                                                         |    0 
 backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java                      |  199 
 web/src/api/promotion.js                                                                          |  136 
 web/src/api/role.ts                                                                               |  156 
 wx/pages/registration/registration.wxml                                                           |    1 
 backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java.backup         | 1589 ++++++++
 /dev/null                                                                                         |   86 
 web/src/api/employee.ts                                                                           |   98 
 web/src/api/judge.js                                                                              |    2 
 backend/src/main/java/com/rongyichuang/activity/dto/ActivityStageInput.java                       |    9 
 101 files changed, 10,280 insertions(+), 1,283 deletions(-)

diff --git a/backend/db.sql b/backend/db.sql
index 63ae387..e5f882b 100644
--- a/backend/db.sql
+++ b/backend/db.sql
@@ -13,6 +13,7 @@
   `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
   `rating_scheme_id` bigint NOT NULL,
   `player_max` int DEFAULT NULL COMMENT '浜烘暟涓婇檺',
+  `sort_order` int DEFAULT NULL COMMENT '闃舵鎺掑簭锛屼粠1寮�濮嬭繛缁�',
   `state` int NOT NULL DEFAULT '1' COMMENT '0:鏈彂甯冿紝 1锛氬彂甯冿細2锛氬叧闂�',
   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `create_user_id` bigint DEFAULT NULL,
@@ -22,6 +23,7 @@
   PRIMARY KEY (`id`) USING BTREE,
   KEY `fk_t_activity_rating_scheme` (`rating_scheme_id`) USING BTREE,
   KEY `idx_t_activity_deadline` (`signup_deadline`) USING BTREE,
+  KEY `idx_t_activity_sort` (`pid`, `sort_order`) USING BTREE,
   CONSTRAINT `fk_t_activity_rating_scheme` FOREIGN KEY (`rating_scheme_id`) REFERENCES `t_rating_scheme` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
 ) ENGINE=InnoDB AUTO_INCREMENT=62 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
diff --git a/backend/src/main/java/com/rongyichuang/activity/dto/ActivityInput.java b/backend/src/main/java/com/rongyichuang/activity/dto/ActivityInput.java
index 3b2317f..41eb4e5 100644
--- a/backend/src/main/java/com/rongyichuang/activity/dto/ActivityInput.java
+++ b/backend/src/main/java/com/rongyichuang/activity/dto/ActivityInput.java
@@ -14,6 +14,7 @@
     private String address;
     private Long ratingSchemeId;
     private Integer playerMax;
+    private Integer sortOrder;
     private Integer state = 1;
     
     // 姣旇禌闃舵鍒楄〃锛堜粎鐢ㄤ簬姣旇禌鍒涘缓/缂栬緫锛�
@@ -98,6 +99,14 @@
         this.playerMax = playerMax;
     }
     
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+    
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+    
     public Integer getState() {
         return state;
     }
diff --git a/backend/src/main/java/com/rongyichuang/activity/dto/ActivityResponse.java b/backend/src/main/java/com/rongyichuang/activity/dto/ActivityResponse.java
index c8cd3e2..7673ca3 100644
--- a/backend/src/main/java/com/rongyichuang/activity/dto/ActivityResponse.java
+++ b/backend/src/main/java/com/rongyichuang/activity/dto/ActivityResponse.java
@@ -19,6 +19,7 @@
     private String address;
     private Long ratingSchemeId;
     private Integer playerMax;
+    private Integer sortOrder;
     private Integer state;
     private LocalDateTime createTime;
     private LocalDateTime updateTime;
@@ -52,6 +53,7 @@
         this.address = activity.getAddress();
         this.ratingSchemeId = activity.getRatingSchemeId();
         this.playerMax = activity.getPlayerMax();
+        this.sortOrder = activity.getSortOrder();
         this.state = activity.getState();
         this.createTime = activity.getCreateTime();
         this.updateTime = activity.getUpdateTime();
@@ -158,6 +160,14 @@
         this.playerMax = playerMax;
     }
     
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+    
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+    
     public Integer getState() {
         return state;
     }
diff --git a/backend/src/main/java/com/rongyichuang/activity/dto/ActivityStageInput.java b/backend/src/main/java/com/rongyichuang/activity/dto/ActivityStageInput.java
index 2b66b52..8445cd0 100644
--- a/backend/src/main/java/com/rongyichuang/activity/dto/ActivityStageInput.java
+++ b/backend/src/main/java/com/rongyichuang/activity/dto/ActivityStageInput.java
@@ -11,6 +11,7 @@
     private String address;
     private Long ratingSchemeId;
     private Integer playerMax;
+    private Integer sortOrder;
     private Integer state = 1;
     
     // 鏋勯�犲嚱鏁�
@@ -73,6 +74,14 @@
         this.playerMax = playerMax;
     }
     
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+    
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+    
     public Integer getState() {
         return state;
     }
diff --git a/backend/src/main/java/com/rongyichuang/activity/entity/Activity.java b/backend/src/main/java/com/rongyichuang/activity/entity/Activity.java
index 8a77723..41ac1e7 100644
--- a/backend/src/main/java/com/rongyichuang/activity/entity/Activity.java
+++ b/backend/src/main/java/com/rongyichuang/activity/entity/Activity.java
@@ -11,7 +11,6 @@
 
 @Entity
 @Table(name = "t_activity")
-@Where(clause = "state = 1")
 public class Activity extends BaseEntity {
     
     @Column(name = "pid", nullable = false)
@@ -42,7 +41,13 @@
     private Integer playerMax;
     
     /**
-     * 鐘舵�侊細1-姝e父锛�0-鍒犻櫎
+     * 闃舵鎺掑簭锛屼粠1寮�濮嬭繛缁�
+     */
+    @Column(name = "sort_order")
+    private Integer sortOrder;
+    
+    /**
+     * 鐘舵�侊細0-鏈彂甯冿紝1-鍙戝竷锛�2-鍏抽棴
      */
     @Column(name = "state", nullable = false)
     private Integer state = 1;
@@ -151,6 +156,14 @@
         this.playerMax = playerMax;
     }
     
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+    
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+    
     public RatingScheme getRatingScheme() {
         return ratingScheme;
     }
diff --git a/backend/src/main/java/com/rongyichuang/activity/entity/ActivityPlayerRating.java b/backend/src/main/java/com/rongyichuang/activity/entity/ActivityPlayerRating.java
index 44a9c24..7977978 100644
--- a/backend/src/main/java/com/rongyichuang/activity/entity/ActivityPlayerRating.java
+++ b/backend/src/main/java/com/rongyichuang/activity/entity/ActivityPlayerRating.java
@@ -63,11 +63,7 @@
     @Column(name = "feedback", columnDefinition = "TEXT")
     private String feedback;
 
-    /**
-     * 璇勫垎鐘舵�侊細0-鏈瘎鍒嗭紝1-宸茶瘎鍒�
-     */
-    @Column(name = "rating_state", nullable = false)
-    private Integer ratingState = 0;
+
 
     /**
      * 鐘舵�侊細1-姝e父锛�0-鍒犻櫎
@@ -154,6 +150,8 @@
         this.feedback = feedback;
     }
 
+
+
     public Integer getState() {
         return state;
     }
@@ -161,17 +159,6 @@
     public void setState(Integer state) {
         this.state = state;
     }
-
-    // 涓轰簡鍏煎鎬э紝淇濈暀status鐩稿叧鏂规硶
-    public Integer getStatus() {
-        return state;
-    }
-
-    public void setStatus(Integer status) {
-        this.state = status;
-    }
-
-
 
     @Override
     public String toString() {
diff --git a/backend/src/main/java/com/rongyichuang/activity/entity/ActivityStatus.java b/backend/src/main/java/com/rongyichuang/activity/entity/ActivityStatus.java
new file mode 100644
index 0000000..8155682
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/activity/entity/ActivityStatus.java
@@ -0,0 +1,17 @@
+package com.rongyichuang.activity.entity;
+
+public enum ActivityStatus {
+    UNPUBLISHED(0),
+    PUBLISHED(1),
+    CLOSED(2);
+
+    private final int value;
+
+    ActivityStatus(int value) {
+        this.value = value;
+    }
+
+    public int getValue() {
+        return value;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java b/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java
index 787a541..c2d9d43 100644
--- a/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java
+++ b/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java
@@ -6,36 +6,94 @@
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
-import org.springframework.stereotype.Repository;
 
 import java.util.List;
 
-@Repository
 public interface ActivityRepository extends JpaRepository<Activity, Long> {
-    
-    // 鏌ヨ姣旇禌鍒楄〃锛坧id=0琛ㄧず姣旇禌锛岄潪0琛ㄧず闃舵锛�
-    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, Integer state, Pageable pageable);
-    
-    // 鎸夊悕绉版ā绯婃煡璇㈡瘮璧涘垪琛�
-    Page<Activity> findByPidAndStateAndNameContainingOrderByCreateTimeDesc(Long pid, Integer state, String name, Pageable pageable);
-    
-    // 鏌ヨ姣旇禌鐨勬墍鏈夐樁娈�
-    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, Integer state);
-    
-    // 鏌ヨ鎵�鏈夋湁鏁堟瘮璧涳紙鐢ㄤ簬涓嬫媺閫夋嫨锛�
-    List<Activity> findByPidAndStateOrderByNameAsc(Long pid, Integer state);
-    
-    // 涓存椂娴嬭瘯锛氭煡璇㈡墍鏈夋瘮璧涳紙涓嶄娇鐢╯tate瀛楁锛�
-    List<Activity> findByPidOrderByNameAsc(Long pid);
-    
-    // 鏌ヨ鎵�鏈夋湁鏁堟椿鍔紙鍖呮嫭姣旇禌鍜岄樁娈碉級锛屾寜pid鍜屽悕绉版帓搴�
-    List<Activity> findByStateOrderByPidAscNameAsc(Integer state);
-    
-    // 缁熻姣旇禌鏁伴噺
-    @Query("SELECT COUNT(a) FROM Activity a WHERE a.pid = 0 AND a.state = 1")
-    long countActiveActivities();
-    
-    // 鏌ヨ杩涜涓殑姣旇禌
-    @Query("SELECT a FROM Activity a WHERE a.pid = 0 AND a.state = 1 AND a.matchTime <= CURRENT_TIMESTAMP AND a.signupDeadline >= CURRENT_TIMESTAMP")
+
+    Page<Activity> findByPidAndStateAndNameContainingOrderByCreateTimeDesc(Long pid, int state, String name, Pageable pageable);
+
+    Page<Activity> findByPidAndNameContainingOrderByCreateTimeDesc(Long pid, String name, Pageable pageable);
+
+    Page<Activity> findByPidOrderByCreateTimeDesc(Long pid, Pageable pageable);
+
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁娲诲姩ID鏌ヨ娲诲姩
+     */
+    @Query("SELECT a FROM Activity a WHERE a.id = :id")
+    Activity findActivityById(@Param("id") Long id);
+
+    /**
+     * 鏍规嵁鐘舵�佹煡璇㈡椿鍔ㄦ暟閲�
+     */
+    @Query("SELECT COUNT(a) FROM Activity a WHERE a.state = :state")
+    Long countByState(@Param("state") int state);
+
+    /**
+     * 缁熻褰撳墠杩涜涓殑娲诲姩鏁伴噺锛堢姸鎬佷负1涓攑id=0鐨勪富娲诲姩锛�
+     */
+    @Query("SELECT COUNT(a) FROM Activity a WHERE a.state = 1 AND a.pid = 0")
+    Long countActiveActivities();
+
+    /**
+     * 鏌ヨ鎵�鏈夌埗绾ф椿鍔紙pid = 0锛�
+     */
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    List<Activity> findAllParentActivities();
+
+    /**
+     * 鏍规嵁鐖剁骇ID鏌ヨ瀛愭椿鍔�
+     */
+    @Query("SELECT a FROM Activity a WHERE a.pid = :pid ORDER BY a.createTime DESC")
+    List<Activity> findChildActivitiesByPid(@Param("pid") Long pid);
+
+    /**
+     * 鏍规嵁鍚嶇О妯$硦鏌ヨ娲诲姩
+     */
+    @Query("SELECT a FROM Activity a WHERE a.name LIKE %:name% ORDER BY a.createTime DESC")
+    List<Activity> findByNameContaining(@Param("name") String name);
+
+    /**
+     * 鏌ヨ鎸囧畾鐘舵�佺殑娲诲姩锛屾寜鍒涘缓鏃堕棿鎺掑簭
+     */
+    @Query("SELECT a FROM Activity a WHERE a.state = :state ORDER BY a.createTime DESC")
+    List<Activity> findByStateOrderByCreateTimeDesc(@Param("state") int state);
+
+    /**
+     * 鍒嗛〉鏌ヨ鎸囧畾鐘舵�佺殑娲诲姩
+     */
+    @Query("SELECT a FROM Activity a WHERE a.state = :state ORDER BY a.createTime DESC")
+    Page<Activity> findByStateOrderByCreateTimeDesc(@Param("state") int state, Pageable pageable);
+
+    /**
+     * 鏌ヨ娲诲姩鍙婂叾璇勫垎鏂规
+     */
+    @Query("SELECT a FROM Activity a LEFT JOIN FETCH a.ratingScheme WHERE a.id = :id")
+    Activity findActivityWithRatingScheme(@Param("id") Long id);
+
+    /**
+     * 鏌ヨ杩涜涓殑娲诲姩锛堢姸鎬佷负1鐨勬椿鍔級
+     */
+    @Query("SELECT a FROM Activity a WHERE a.state = 1 ORDER BY a.createTime DESC")
     List<Activity> findOngoingActivities();
+
+    /**
+     * 鏌ユ壘鎸囧畾娲诲姩鐨勭涓�闃舵锛坰ortOrder=1锛�
+     */
+    @Query("SELECT a FROM Activity a WHERE a.pid = :activityId AND a.sortOrder = 1 AND a.state = 1")
+    Activity findFirstStageByActivityId(@Param("activityId") Long activityId);
+
+    /**
+     * 鏍规嵁鐖剁骇ID鍜岀姸鎬佹煡璇㈡椿鍔紝鎸塻ortOrder鎺掑簭
+     */
+    List<Activity> findByPidAndStateOrderBySortOrderAsc(Long pid, Integer state);
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java.backup b/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java.backup
new file mode 100644
index 0000000..d67f2b3
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java.backup
@@ -0,0 +1,1589 @@
+package com.rongyichuang.activity.repository;
+
+import com.rongyichuang.activity.entity.Activity;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface ActivityRepository extends JpaRepository<Activity, Long> {
+
+    Page<Activity> findByPidAndStateAndNameContainingOrderByCreateTimeDesc(Long pid, int state, String name, Pageable pageable);
+
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     */
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
+
+    List<Activity> findByStateOrderByPidAscNameAsc(int state);
+
+    @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
+    Page<Activity> findRecentActivities(Pageable pageable);
+
+    /**
+     * 鏍规嵁ID鍒楄〃鏌ヨ姣旇禌
+     *
+    Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    List
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java b/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
index 508e233..a0ad78b 100644
--- a/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
+++ b/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
@@ -10,12 +10,15 @@
 import com.rongyichuang.activity.repository.ActivityJudgeRepository;
 import com.rongyichuang.activity.repository.ActivityRepository;
 import com.rongyichuang.judge.entity.Judge;
+import com.rongyichuang.player.repository.ActivityPlayerRepository;
 import com.rongyichuang.judge.repository.JudgeRepository;
 import com.rongyichuang.common.dto.PageRequest;
 import com.rongyichuang.common.dto.PageResponse;
 import com.rongyichuang.rating.entity.RatingScheme;
 import com.rongyichuang.rating.repository.RatingSchemeRepository;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
@@ -23,9 +26,12 @@
 import org.springframework.util.StringUtils;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 @Service
@@ -44,21 +50,32 @@
     @Autowired
     private RatingSchemeRepository ratingSchemeRepository;
     
+    @Autowired
+    private ActivityPlayerRepository activityPlayerRepository;
+    
     /**
      * 鍒嗛〉鏌ヨ姣旇禌鍒楄〃
      */
     public PageResponse<ActivityResponse> findActivities(PageRequest pageRequest, String name) {
         Pageable pageable = pageRequest.toPageable();
         Page<Activity> page;
-        
+
         if (StringUtils.hasText(name)) {
-            page = activityRepository.findByPidAndStateAndNameContainingOrderByCreateTimeDesc(0L, 1, name, pageable);
+            page = activityRepository.findByPidAndNameContainingOrderByCreateTimeDesc(0L, name, pageable);
         } else {
-            page = activityRepository.findByPidAndStateOrderByCreateTimeDesc(0L, 1, pageable);
+            // 鏌ヨ鎵�鏈変富娲诲姩锛坧id = 0锛�
+            page = activityRepository.findByPidOrderByCreateTimeDesc(0L, pageable);
         }
-        
+
         List<ActivityResponse> content = page.getContent().stream()
-            .map(ActivityResponse::new)
+            .map(activity -> {
+                ActivityResponse response = new ActivityResponse(activity);
+                // 璁剧疆鍙傝禌浜烘暟锛堝鏍搁�氳繃鐨勬姤鍚嶆暟閲忥級
+                Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
+                int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
+                response.setPlayerCount(playerCount);
+                return response;
+            })
             .collect(Collectors.toList());
         
         return new PageResponse<>(content, page.getTotalElements(), page.getNumber(), page.getSize());
@@ -73,11 +90,23 @@
             Activity activity = activityOpt.get();
             ActivityResponse response = new ActivityResponse(activity);
             
+            // 璁剧疆鍙傝禌浜烘暟锛堝鏍搁�氳繃鐨勬姤鍚嶆暟閲忥級
+            Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
+            int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
+            response.setPlayerCount(playerCount);
+            
             // 濡傛灉鏄瘮璧涳紝鍔犺浇鍏堕樁娈�
             if (activity.isMainActivity()) {
                 List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(id, 1);
                 List<ActivityResponse> stageResponses = stages.stream()
-                    .map(ActivityResponse::new)
+                    .map(stage -> {
+                        ActivityResponse stageResponse = new ActivityResponse(stage);
+                        // 璁剧疆闃舵鐨勫弬璧涗汉鏁�
+                        Long stagePlayerCountLong = activityPlayerRepository.countByActivityId(stage.getId());
+                        int stagePlayerCount = stagePlayerCountLong != null ? stagePlayerCountLong.intValue() : 0;
+                        stageResponse.setPlayerCount(stagePlayerCount);
+                        return stageResponse;
+                    })
                     .collect(Collectors.toList());
                 response.setStages(stageResponses);
             }
@@ -127,6 +156,21 @@
             if (!schemeOpt.isPresent()) {
                 throw new RuntimeException("璇勫垎妯℃澘涓嶅瓨鍦�");
             }
+        }
+        
+        // 濡傛灉鏄瘮璧涳紝楠岃瘉蹇呴』鑷冲皯鏈変竴涓樁娈�
+        if (input.isMainActivity()) {
+            if (input.getStages() == null || input.getStages().isEmpty()) {
+                throw new RuntimeException("姣旇禌蹇呴』鑷冲皯鏈変竴涓樁娈�");
+            }
+            
+            // 楠岃瘉闃舵鏁伴噺涓嶈秴杩�5涓�
+            if (input.getStages().size() > 5) {
+                throw new RuntimeException("姣旇禌闃舵鏁伴噺涓嶈兘瓒呰繃5涓�");
+            }
+            
+            // 楠岃瘉sort_order杩炵画鎬�
+            validateSortOrderContinuity(input.getStages());
         }
         
         // 淇濆瓨姣旇禌
@@ -198,6 +242,7 @@
             stage.setMatchTime(stageInput.getMatchTime());
             stage.setAddress(stageInput.getAddress());
             stage.setPlayerMax(stageInput.getPlayerMax());
+            stage.setSortOrder(stageInput.getSortOrder());
             stage.setState(stageInput.getState());
             
             // 闃舵缁ф壙姣旇禌鐨勬姤鍚嶆埅姝㈡椂闂�
@@ -246,7 +291,9 @@
             } else {
                 // 涓烘瘡涓寚瀹氱殑闃舵鍒涘缓鍏宠仈
                 for (Long stageId : judgeInput.getStageIds()) {
-                    ActivityJudge activityJudge = new ActivityJudge(activityId, judgeInput.getJudgeId(), stageId);
+                    // 濡傛灉stageId绛変簬褰撳墠姣旇禌ID锛岃〃绀鸿瘎濮旇礋璐f暣涓瘮璧涳紝stage_id璁句负null
+                    Long actualStageId = stageId.equals(activityId) ? null : stageId;
+                    ActivityJudge activityJudge = new ActivityJudge(activityId, judgeInput.getJudgeId(), actualStageId);
                     activityJudgeRepository.save(activityJudge);
                 }
             }
@@ -279,61 +326,46 @@
     }
     
     /**
-     * 鑾峰彇鎵�鏈夋湁鏁堟瘮璧涘拰闃舵锛堢敤浜庝笅鎷夐�夋嫨锛�
+     * 鑾峰彇鎵�鏈夋湁鏁堜富姣旇禌锛堢敤浜庝笅鎷夐�夋嫨锛�
      */
     public List<ActivityResponse> findAllActivitiesForSelection() {
-        // 鑾峰彇鎵�鏈夋椿鍔紙鍖呮嫭姣旇禌鍜岄樁娈碉級
-        List<Activity> activities = activityRepository.findByStateOrderByPidAscNameAsc(1);
+        // 鑾峰彇鎵�鏈夌姸鎬佷负1鐨勬椿鍔�
+        List<Activity> allActivities = activityRepository.findByStateOrderByPidAscNameAsc(1);
         
-        // 鍒涘缓姣旇禌ID鍒版瘮璧涘璞$殑鏄犲皠锛岀敤浜庡揩閫熸煡鎵剧埗姣旇禌
-        Map<Long, Activity> mainActivityMap = activities.stream()
-            .filter(activity -> activity.getPid() == 0)
-            .collect(Collectors.toMap(Activity::getId, activity -> activity));
-        
-        // 杞崲涓篈ctivityResponse骞跺~鍏卲arent淇℃伅
-        List<ActivityResponse> result = activities.stream()
-            .map(activity -> {
-                ActivityResponse response = new ActivityResponse(activity);
-                // 濡傛灉鏄樁娈碉紙pid > 0锛夛紝濉厖parent淇℃伅
-                if (activity.getPid() > 0) {
-                    Activity parentActivity = mainActivityMap.get(activity.getPid());
-                    if (parentActivity != null) {
-                        response.setParent(new ActivityResponse(parentActivity));
-                    }
-                }
-                return response;
-            })
+        // 杩囨护锛氬彧淇濈暀姣旇禌闃舵锛坧id>0锛�
+        List<Activity> filteredActivities = allActivities.stream()
+            .filter(activity -> activity.getPid() > 0)
             .collect(Collectors.toList());
         
-        // 鑷畾涔夋帓搴忥細姣旇禌鍜屽叾闃舵鏀惧湪涓�璧�
-        result.sort((a, b) -> {
-            // 濡傛灉閮芥槸姣旇禌锛坧id=0锛夛紝鎸夊悕绉版帓搴�
-            if (a.getPid() == 0 && b.getPid() == 0) {
-                return a.getName().compareTo(b.getName());
-            }
-            // 濡傛灉閮芥槸闃舵锛屽厛鎸夌埗姣旇禌鍚嶇О鎺掑簭锛屽啀鎸夐樁娈靛悕绉版帓搴�
-            if (a.getPid() > 0 && b.getPid() > 0) {
-                String aParentName = a.getParent() != null ? a.getParent().getName() : "";
-                String bParentName = b.getParent() != null ? b.getParent().getName() : "";
-                int parentCompare = aParentName.compareTo(bParentName);
-                if (parentCompare != 0) {
-                    return parentCompare;
+        // 杞崲涓篈ctivityResponse锛屽寘鍚埗姣旇禌淇℃伅
+        List<ActivityResponse> result = filteredActivities.stream()
+            .map(activity -> {
+                ActivityResponse response = new ActivityResponse(activity);
+                // 璁剧疆鍙傝禌浜烘暟锛堝鏍搁�氳繃鐨勬姤鍚嶆暟閲忥級
+                Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
+                int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
+                response.setPlayerCount(playerCount);
+                
+                // 璁剧疆鐖舵瘮璧涗俊鎭�
+                Optional<Activity> parentOpt = activityRepository.findById(activity.getPid());
+                if (parentOpt.isPresent()) {
+                    Activity parent = parentOpt.get();
+                    response.setParent(new ActivityResponse(parent));
+                }
+                
+                return response;
+            })
+            .sorted((a, b) -> {
+                // 鍏堟寜鐖舵瘮璧涘悕绉版帓搴忥紝鍐嶆寜闃舵鍚嶇О鎺掑簭
+                if (a.getParent() != null && b.getParent() != null) {
+                    int parentCompare = a.getParent().getName().compareTo(b.getParent().getName());
+                    if (parentCompare != 0) {
+                        return parentCompare;
+                    }
                 }
                 return a.getName().compareTo(b.getName());
-            }
-            // 濡傛灉涓�涓槸姣旇禌锛屼竴涓槸闃舵
-            if (a.getPid() == 0 && b.getPid() > 0) {
-                String bParentName = b.getParent() != null ? b.getParent().getName() : "";
-                int compare = a.getName().compareTo(bParentName);
-                return compare <= 0 ? -1 : 1; // 姣旇禌鎺掑湪鍏堕樁娈靛墠闈�
-            }
-            if (a.getPid() > 0 && b.getPid() == 0) {
-                String aParentName = a.getParent() != null ? a.getParent().getName() : "";
-                int compare = aParentName.compareTo(b.getName());
-                return compare < 0 ? -1 : 1; // 闃舵鎺掑湪鍏舵瘮璧涘悗闈�
-            }
-            return 0;
-        });
+            })
+            .collect(Collectors.toList());
         
         return result;
     }
@@ -344,7 +376,14 @@
     public List<ActivityResponse> findStagesByActivityId(Long activityId) {
         List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(activityId, 1);
         return stages.stream()
-            .map(ActivityResponse::new)
+            .map(activity -> {
+                ActivityResponse response = new ActivityResponse(activity);
+                // 璁剧疆鍙傝禌浜烘暟锛堝鏍搁�氳繃鐨勬姤鍚嶆暟閲忥級
+                Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
+                int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
+                response.setPlayerCount(playerCount);
+                return response;
+            })
             .collect(Collectors.toList());
     }
     
@@ -361,7 +400,14 @@
     public List<ActivityResponse> findOngoingActivities() {
         List<Activity> activities = activityRepository.findOngoingActivities();
         return activities.stream()
-            .map(ActivityResponse::new)
+            .map(activity -> {
+                ActivityResponse response = new ActivityResponse(activity);
+                // 璁剧疆鍙傝禌浜烘暟锛堝鏍搁�氳繃鐨勬姤鍚嶆暟閲忥級
+                Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
+                int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
+                response.setPlayerCount(playerCount);
+                return response;
+            })
             .collect(Collectors.toList());
     }
     
@@ -391,7 +437,10 @@
         Map<Long, List<Long>> judgeStageMap = activityJudges.stream()
             .collect(Collectors.groupingBy(
                 ActivityJudge::getJudgeId,
-                Collectors.mapping(ActivityJudge::getStageId, Collectors.toList())
+                Collectors.mapping(
+                    aj -> aj.getStageId() != null ? aj.getStageId() : activityId, // 濡傛灉stage_id涓簄ull锛岃〃绀鸿礋璐f暣涓瘮璧涳紝浣跨敤activityId
+                    Collectors.toList()
+                )
             ));
         
         // 鏌ヨ璇勫璇︾粏淇℃伅骞舵瀯寤哄搷搴�
@@ -416,4 +465,38 @@
         
         return result;
     }
+    
+    /**
+     * 楠岃瘉闃舵鐨剆ort_order杩炵画鎬�
+     */
+    private void validateSortOrderContinuity(List<ActivityStageInput> stages) {
+        if (stages == null || stages.isEmpty()) {
+            return;
+        }
+        
+        // 鏀堕泦鎵�鏈夌殑sortOrder鍊�
+        List<Integer> sortOrders = stages.stream()
+            .map(ActivityStageInput::getSortOrder)
+            .filter(Objects::nonNull)
+            .sorted()
+            .collect(Collectors.toList());
+        
+        // 妫�鏌ユ槸鍚︿粠1寮�濮�
+        if (sortOrders.isEmpty() || sortOrders.get(0) != 1) {
+            throw new RuntimeException("闃舵鎺掑簭蹇呴』浠�1寮�濮�");
+        }
+        
+        // 妫�鏌ヨ繛缁��
+        for (int i = 0; i < sortOrders.size(); i++) {
+            if (sortOrders.get(i) != i + 1) {
+                throw new RuntimeException("闃舵鎺掑簭蹇呴』杩炵画锛屼笉鑳借烦璺冿紙濡傦細1,2,3...锛�");
+            }
+        }
+        
+        // 妫�鏌ユ槸鍚︽湁閲嶅鐨剆ortOrder
+        Set<Integer> uniqueSortOrders = new HashSet<>(sortOrders);
+        if (uniqueSortOrders.size() != sortOrders.size()) {
+            throw new RuntimeException("闃舵鎺掑簭涓嶈兘閲嶅");
+        }
+    }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java b/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..32c4aa5
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/auth/filter/JwtAuthenticationFilter.java
@@ -0,0 +1,86 @@
+package com.rongyichuang.auth.filter;
+
+import com.rongyichuang.auth.util.JwtUtil;
+import com.rongyichuang.user.entity.User;
+import com.rongyichuang.user.repository.UserRepository;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Optional;
+
+/**
+ * JWT璁よ瘉杩囨护鍣�
+ */
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+
+    @Autowired
+    private JwtUtil jwtUtil;
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
+                                  FilterChain filterChain) throws ServletException, IOException {
+        
+        String authHeader = request.getHeader("Authorization");
+        String token = null;
+        Long userId = null;
+
+        // 浠庤姹傚ご涓彁鍙朖WT token
+        if (authHeader != null && authHeader.startsWith("Bearer ")) {
+            token = authHeader.substring(7);
+            try {
+                userId = jwtUtil.getUserIdFromToken(token);
+            } catch (Exception e) {
+                logger.debug("JWT token瑙f瀽澶辫触: {}", e.getMessage());
+            }
+        }
+
+        // 濡傛灉token鏈夋晥涓斿綋鍓嶆病鏈夎璇佷俊鎭�
+        if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+            
+            // 楠岃瘉token鏄惁鏈夋晥
+            if (jwtUtil.validateToken(token)) {
+                
+                // 鏌ユ壘鐢ㄦ埛淇℃伅
+                Optional<User> userOpt = userRepository.findById(userId);
+                if (userOpt.isPresent()) {
+                    User user = userOpt.get();
+                    
+                    // 鍒涘缓璁よ瘉瀵硅薄
+                    UsernamePasswordAuthenticationToken authToken = 
+                        new UsernamePasswordAuthenticationToken(
+                            user.getId().toString(), 
+                            null, 
+                            new ArrayList<>() // 鏆傛椂涓嶈缃潈闄愶紝鍚庣画鍙互鏍规嵁瑙掕壊璁剧疆
+                        );
+                    
+                    authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+                    SecurityContextHolder.getContext().setAuthentication(authToken);
+                    
+                    logger.debug("鐢ㄦ埛璁よ瘉鎴愬姛: userId={}, phone={}", user.getId(), user.getPhone());
+                }
+            }
+        }
+
+        filterChain.doFilter(request, response);
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/common/api/DataCleanupController.java b/backend/src/main/java/com/rongyichuang/common/api/DataCleanupController.java
index d3cdf9f..4be41a6 100644
--- a/backend/src/main/java/com/rongyichuang/common/api/DataCleanupController.java
+++ b/backend/src/main/java/com/rongyichuang/common/api/DataCleanupController.java
@@ -6,6 +6,9 @@
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.HashMap;
+import java.util.Map;
+
 @RestController
 @RequestMapping("/cleanup")
 public class DataCleanupController {
@@ -23,4 +26,41 @@
             return "娓呯悊澶辫触: " + e.getMessage();
         }
     }
+
+    @PostMapping("/clear-all-test-data")
+    public Map<String, Object> clearAllTestData() {
+        Map<String, Object> result = new HashMap<>();
+        try {
+            // 绂佺敤澶栭敭妫�鏌�
+            jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 0");
+            
+            // 鎸夌収澶栭敭渚濊禆鍏崇郴鐨勯『搴忓垹闄�
+            int deletedRatingItems = jdbcTemplate.update("DELETE FROM t_activity_rating_item");
+            int deletedRatings = jdbcTemplate.update("DELETE FROM t_activity_rating");
+            int deletedActivityPlayers = jdbcTemplate.update("DELETE FROM t_activity_player");
+            int deletedActivityJudges = jdbcTemplate.update("DELETE FROM t_activity_judge");
+            int deletedPlayers = jdbcTemplate.update("DELETE FROM t_player");
+            int deletedActivities = jdbcTemplate.update("DELETE FROM t_activity");
+            
+            // 閲嶆柊鍚敤澶栭敭妫�鏌�
+            jdbcTemplate.execute("SET FOREIGN_KEY_CHECKS = 1");
+            
+            result.put("success", true);
+            result.put("message", "鎵�鏈夋祴璇曟暟鎹凡娓呯┖");
+            result.put("deletedCounts", Map.of(
+                "t_activity_rating_item", deletedRatingItems,
+                "t_activity_rating", deletedRatings,
+                "t_activity_player", deletedActivityPlayers,
+                "t_activity_judge", deletedActivityJudges,
+                "t_player", deletedPlayers,
+                "t_activity", deletedActivities
+            ));
+            
+        } catch (Exception e) {
+            result.put("success", false);
+            result.put("message", "娓呯悊澶辫触: " + e.getMessage());
+        }
+        
+        return result;
+    }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/common/enums/MediaTargetType.java b/backend/src/main/java/com/rongyichuang/common/enums/MediaTargetType.java
index 1a2f481..04bce05 100644
--- a/backend/src/main/java/com/rongyichuang/common/enums/MediaTargetType.java
+++ b/backend/src/main/java/com/rongyichuang/common/enums/MediaTargetType.java
@@ -28,11 +28,6 @@
     ACTIVITY_PLAYER_SUBMISSION(5, "鍙傝禌鎶ュ悕璧勬枡"),
     
     /**
-     * 瀛﹀憳澶村儚
-     */
-    STUDENT_AVATAR(6, "瀛﹀憳澶村儚"),
-    
-    /**
      * 鐢ㄦ埛澶村儚
      */
     USER_AVATAR(7, "鐢ㄦ埛澶村儚");
diff --git a/backend/src/main/java/com/rongyichuang/common/util/UserContextUtil.java b/backend/src/main/java/com/rongyichuang/common/util/UserContextUtil.java
index 0bf65c7..e659917 100644
--- a/backend/src/main/java/com/rongyichuang/common/util/UserContextUtil.java
+++ b/backend/src/main/java/com/rongyichuang/common/util/UserContextUtil.java
@@ -56,8 +56,19 @@
             if (authentication != null && authentication.isAuthenticated() && 
                 !"anonymousUser".equals(authentication.getPrincipal())) {
                 logger.debug("鑾峰彇鍒拌璇佺敤鎴�: {}", authentication.getName());
-                // 濡傛灉璁よ瘉淇℃伅涓寘鍚敤鎴稩D锛屽彲浠ュ湪杩欓噷瑙f瀽
-                // 鏆傛椂杩斿洖鍥哄畾鐢ㄦ埛ID鐢ㄤ簬鍏煎鎬�
+                // 鍦ㄥ紑鍙戠幆澧冧笅锛岃繑鍥炰竴涓湁鏁堢殑璇勫鐢ㄦ埛ID
+                // 鏌ユ壘绗竴涓湁鏁堢殑璇勫璁板綍骞惰繑鍥炲叾user_id
+                try {
+                    Optional<Judge> firstJudge = judgeRepository.findAll().stream().findFirst();
+                    if (firstJudge.isPresent() && firstJudge.get().getUserId() != null) {
+                        Long userId = firstJudge.get().getUserId();
+                        logger.debug("寮�鍙戠幆澧冿細浣跨敤璇勫鐢ㄦ埛ID: {}", userId);
+                        return userId;
+                    }
+                } catch (Exception e) {
+                    logger.warn("鏌ユ壘璇勫鐢ㄦ埛ID鏃跺彂鐢熷紓甯�: {}", e.getMessage());
+                }
+                // 濡傛灉娌℃湁鎵惧埌璇勫锛岃繑鍥炲浐瀹氱敤鎴稩D
                 return 1L;
             }
         } catch (Exception e) {
diff --git a/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java b/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
index 2e1b542..1ae6ced 100644
--- a/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
+++ b/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
@@ -1,5 +1,7 @@
 package com.rongyichuang.config;
 
+import com.rongyichuang.auth.filter.JwtAuthenticationFilter;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -11,6 +13,7 @@
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.CorsConfigurationSource;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -24,6 +27,9 @@
 @EnableWebSecurity
 @EnableMethodSecurity
 public class SecurityConfig {
+
+    @Autowired
+    private JwtAuthenticationFilter jwtAuthenticationFilter;
 
     @Bean
     public PasswordEncoder passwordEncoder() {
@@ -42,14 +48,12 @@
             .csrf(csrf -> csrf.disable())
             .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
             .authorizeHttpRequests(auth -> auth
-                .requestMatchers("/api/auth/**").permitAll()
-                .requestMatchers("/api/graphql/**").permitAll()
-                .requestMatchers("/graphql/**").permitAll()
-                .requestMatchers("/graphql").permitAll()
-                .requestMatchers("/api/graphiql").permitAll()
-                .requestMatchers("/api/test/**").permitAll()
-                .anyRequest().permitAll()
-            );
+                .requestMatchers("/api/auth/**", "/api/actuator/**", "/api/test/**", "/api/cleanup/**").permitAll()
+                .requestMatchers("/api/graphql", "/api/graphql/**", "/api/graphiql").permitAll()
+                .requestMatchers("/graphql", "/graphql/**").permitAll()
+                .anyRequest().authenticated()
+            )
+            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
 
         return http.build();
     }
diff --git a/backend/src/main/java/com/rongyichuang/media/api/MediaV2GraphqlApi.java b/backend/src/main/java/com/rongyichuang/media/api/MediaV2GraphqlApi.java
index 3a280cf..d154bbd 100644
--- a/backend/src/main/java/com/rongyichuang/media/api/MediaV2GraphqlApi.java
+++ b/backend/src/main/java/com/rongyichuang/media/api/MediaV2GraphqlApi.java
@@ -39,17 +39,24 @@
      * @return 淇濆瓨缁撴灉
      */
     @MutationMapping
-    public MediaSaveResponse savePlayerAvatar(@Argument Long playerId, @Argument String url, 
+    public MediaSaveResponse savePlayerAvatar(@Argument Long playerId, @Argument String path, 
                                             @Argument String fileName, @Argument Long fileSize) {
         log.info("鏀跺埌淇濆瓨閫夋墜澶村儚璇锋眰锛岄�夋墜ID: {}", playerId);
         
         MediaSaveInput input = new MediaSaveInput();
         input.setTargetType("player");
         input.setTargetId(playerId);
-        input.setPath(url);
-        input.setName(fileName);
+        input.setPath(path);
+        input.setFileName(fileName);
         input.setFileSize(fileSize);
-        input.setMediaType(1); // 澶村儚绫诲瀷
+        input.setMediaType(1); // 澶村儚榛樿涓哄浘鐗囩被鍨�
+        
+        // 浠庢枃浠跺悕涓彁鍙栨枃浠舵墿灞曞悕
+        String fileExt = "";
+        if (fileName != null && fileName.contains(".")) {
+            fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
+        }
+        input.setFileExt(fileExt);
         
         return mediaService.saveMedia(input);
     }
@@ -64,19 +71,19 @@
      * @return 淇濆瓨缁撴灉
      */
     @MutationMapping
-    public MediaSaveResponse saveActivityPlayerAttachment(@Argument Long activityPlayerId, @Argument String url,
-                                                        @Argument String fileName, @Argument Long fileSize,
-                                                        @Argument Integer mediaType) {
+    public MediaSaveResponse saveActivityPlayerAttachment(@Argument Long activityPlayerId, @Argument String path,
+                                                        @Argument String fileName, @Argument Long fileSize, @Argument Integer mediaType) {
         log.info("鏀跺埌淇濆瓨娲诲姩鎶ュ悕闄勪欢璇锋眰锛屾椿鍔ㄦ姤鍚岻D: {}", activityPlayerId);
         
         MediaSaveInput input = new MediaSaveInput();
         input.setTargetType("activity_player");
         input.setTargetId(activityPlayerId);
-        input.setPath(url);
-        input.setName(fileName);
+        input.setPath(path);
+        input.setFileName(fileName);
         input.setFileSize(fileSize);
         input.setMediaType(mediaType);
         
+        
         return mediaService.saveMedia(input);
     }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/media/dto/MediaSaveInput.java b/backend/src/main/java/com/rongyichuang/media/dto/MediaSaveInput.java
index b133951..dc411a7 100644
--- a/backend/src/main/java/com/rongyichuang/media/dto/MediaSaveInput.java
+++ b/backend/src/main/java/com/rongyichuang/media/dto/MediaSaveInput.java
@@ -7,8 +7,8 @@
     
     private String targetType; // 鐩爣绫诲瀷锛歱layer, activity_player
     private Long targetId; // 鐩爣ID
-    private String url; // COS鏂囦欢URL
-    private String thumbUrl; // 缂╃暐鍥綰RL锛堝彲閫夛級
+    private String path; // COS鏂囦欢璺緞
+    private String thumbPath; // 缂╃暐鍥捐矾寰勶紙鍙�夛級
     private String fileName; // 鏂囦欢鍚�
     private String fileExt; // 鏂囦欢鎵╁睍鍚�
     private Long fileSize; // 鏂囦欢澶у皬锛堝瓧鑺傦級
@@ -36,20 +36,20 @@
         this.targetId = targetId;
     }
     
-    public String getUrl() {
-        return url;
+    public String getPath() {
+        return path;
     }
     
-    public void setUrl(String url) {
-        this.url = url;
+    public void setPath(String path) {
+        this.path = path;
     }
     
-    public String getThumbUrl() {
-        return thumbUrl;
+    public String getThumbPath() {
+        return thumbPath;
     }
     
-    public void setThumbUrl(String thumbUrl) {
-        this.thumbUrl = thumbUrl;
+    public void setThumbPath(String thumbPath) {
+        this.thumbPath = thumbPath;
     }
     
     public String getFileName() {
diff --git a/backend/src/main/java/com/rongyichuang/media/service/MediaV2Service.java b/backend/src/main/java/com/rongyichuang/media/service/MediaV2Service.java
index b1329cf..7c6cf58 100644
--- a/backend/src/main/java/com/rongyichuang/media/service/MediaV2Service.java
+++ b/backend/src/main/java/com/rongyichuang/media/service/MediaV2Service.java
@@ -9,6 +9,8 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 /**
  * 濯掍綋鏈嶅姟绫�
  */
@@ -37,32 +39,58 @@
             if (input.getTargetId() == null) {
                 return MediaSaveResponse.error("鐩爣ID涓嶈兘涓虹┖");
             }
-            if (input.getUrl() == null || input.getUrl().trim().isEmpty()) {
-                return MediaSaveResponse.error("鏂囦欢URL涓嶈兘涓虹┖");
+            if (input.getPath() == null || input.getPath().trim().isEmpty()) {
+                return MediaSaveResponse.error("鏂囦欢璺緞涓嶈兘涓虹┖");
             }
             if (input.getMediaType() == null) {
                 return MediaSaveResponse.error("濯掍綋绫诲瀷涓嶈兘涓虹┖");
             }
             
-            // 鍒涘缓Media瀹炰綋
-            Media media = new Media();
-            
             // 灏唗argetType瀛楃涓茶浆鎹负鏁存暟
             Integer targetTypeInt = convertTargetTypeToInt(input.getTargetType());
-            media.setTargetType(targetTypeInt);
-            media.setTargetId(input.getTargetId());
-            media.setPath(input.getUrl());
-            media.setThumbPath(input.getThumbUrl());
+            
+            Media media;
+            boolean isUpdate = false;
+            
+            // 鏍规嵁鐩爣绫诲瀷鍐冲畾澶勭悊绛栫暐
+            if (targetTypeInt == 7) { // USER_AVATAR - 澶村儚锛岄渶瑕佹洿鏂扮幇鏈夎褰�
+                List<Media> existingMedias = mediaRepository.findByTargetTypeAndTargetIdAndState(
+                    targetTypeInt, input.getTargetId(), 1);
+                
+                if (!existingMedias.isEmpty()) {
+                    // 瀛樺湪澶村儚璁板綍锛屾洿鏂扮涓�涓褰�
+                    media = existingMedias.get(0);
+                    isUpdate = true;
+                    log.info("鎵惧埌鐜版湁澶村儚璁板綍锛屽皢杩涜鏇存柊锛孖D: {}", media.getId());
+                } else {
+                    // 涓嶅瓨鍦ㄥご鍍忚褰曪紝鍒涘缓鏂拌褰�
+                    media = new Media();
+                    media.setTargetType(targetTypeInt);
+                    media.setTargetId(input.getTargetId());
+                    media.setState(1);
+                    log.info("鏈壘鍒扮幇鏈夊ご鍍忚褰曪紝灏嗗垱寤烘柊鐨勫ご鍍忚褰�");
+                }
+            } else {
+                // 鍏朵粬绫诲瀷锛堝闄勪欢锛夛紝鎬绘槸鍒涘缓鏂拌褰�
+                media = new Media();
+                media.setTargetType(targetTypeInt);
+                media.setTargetId(input.getTargetId());
+                media.setState(1); // 1琛ㄧず鏈夋晥鐘舵��
+                log.info("鍒涘缓鏂扮殑濯掍綋璁板綍锛岀被鍨�: {}", input.getTargetType());
+            }
+            
+            // 鏇存柊鎴栬缃獟浣撲俊鎭�
+            media.setPath(input.getPath());
+            media.setThumbPath(input.getThumbPath());
             media.setName(input.getFileName());
             media.setFileExt(input.getFileExt());
             media.setFileSize(input.getFileSize() != null ? input.getFileSize().intValue() : null);
-        media.setDuration(input.getDuration());
-        media.setMediaType(input.getMediaType());
-        media.setState(1); // 1琛ㄧず鏈夋晥鐘舵��
+            media.setDuration(input.getDuration());
+            media.setMediaType(input.getMediaType());
             
             // 淇濆瓨鍒版暟鎹簱
             Media savedMedia = mediaRepository.save(media);
-            log.info("濯掍綋鏂囦欢淇濆瓨鎴愬姛锛孖D: {}", savedMedia.getId());
+            log.info("濯掍綋鏂囦欢{}鎴愬姛锛孖D: {}", isUpdate ? "鏇存柊" : "淇濆瓨", savedMedia.getId());
             
             return MediaSaveResponse.success("濯掍綋鏂囦欢淇濆瓨鎴愬姛", savedMedia.getId());
             
@@ -82,9 +110,9 @@
     private Integer convertTargetTypeToInt(String targetType) {
         switch (targetType) {
             case "player":
-                return 1;
+                return 7; // USER_AVATAR - 鐢ㄦ埛澶村儚
             case "activity_player":
-                return 2;
+                return 5; // ACTIVITY_PLAYER_SUBMISSION - 鍙傝禌鎶ュ悕璧勬枡
             default:
                 throw new IllegalArgumentException("涓嶆敮鎸佺殑鐩爣绫诲瀷: " + targetType);
         }
diff --git a/backend/src/main/java/com/rongyichuang/message/entity/Message.java b/backend/src/main/java/com/rongyichuang/message/entity/Message.java
new file mode 100644
index 0000000..94ddbe1
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/message/entity/Message.java
@@ -0,0 +1,154 @@
+package com.rongyichuang.message.entity;
+
+import com.rongyichuang.common.entity.BaseEntity;
+import jakarta.persistence.*;
+
+/**
+ * 娑堟伅瀹炰綋绫�
+ * 瀵瑰簲鏁版嵁搴撹〃锛歵_msg
+ */
+@Entity
+@Table(name = "t_msg")
+public class Message extends BaseEntity {
+
+    /**
+     * 鐩爣绫诲瀷
+     */
+    @Column(name = "target_type", nullable = false)
+    private Integer targetType;
+
+    /**
+     * 鐩爣ID
+     */
+    @Column(name = "target_id", nullable = false)
+    private Integer targetId;
+
+    /**
+     * 瀛﹀憳ID
+     */
+    @Column(name = "player_id", nullable = false)
+    private Integer playerId;
+
+    /**
+     * 鐢ㄦ埛ID
+     */
+    @Column(name = "user_id", nullable = false)
+    private Integer userId;
+
+    /**
+     * 娑堟伅鍐呭
+     */
+    @Column(name = "content", length = 200, nullable = false)
+    private String content;
+
+    /**
+     * 妯℃澘鍐呭
+     */
+    @Column(name = "template_content", length = 200)
+    private String templateContent;
+
+    /**
+     * 寰俊娑堟伅鍙戦�佹垚鍔熸爣蹇�
+     */
+    @Column(name = "wx_msg_success", nullable = false)
+    private Boolean wxMsgSuccess;
+
+    /**
+     * 寰俊娑堟伅閿欒娆℃暟
+     */
+    @Column(name = "wx_msg_err_count", nullable = false)
+    private Integer wxMsgErrCount = 0;
+
+    /**
+     * 寰俊娑堟伅鏈�鍚庨敊璇俊鎭�
+     */
+    @Column(name = "wx_msg_last_err", length = 255)
+    private String wxMsgLastErr;
+
+    /**
+     * 鐘舵�侊細0-鏆傛椂涓嶅彂甯冿紝1-鍙互鍙戝竷锛�2-宸茬粡鍙戝竷
+     */
+    @Column(name = "state", nullable = false)
+    private Integer state;
+
+    // Getters and Setters
+    public Integer getTargetType() {
+        return targetType;
+    }
+
+    public void setTargetType(Integer targetType) {
+        this.targetType = targetType;
+    }
+
+    public Integer getTargetId() {
+        return targetId;
+    }
+
+    public void setTargetId(Integer targetId) {
+        this.targetId = targetId;
+    }
+
+    public Integer getPlayerId() {
+        return playerId;
+    }
+
+    public void setPlayerId(Integer playerId) {
+        this.playerId = playerId;
+    }
+
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public String getTemplateContent() {
+        return templateContent;
+    }
+
+    public void setTemplateContent(String templateContent) {
+        this.templateContent = templateContent;
+    }
+
+    public Boolean getWxMsgSuccess() {
+        return wxMsgSuccess;
+    }
+
+    public void setWxMsgSuccess(Boolean wxMsgSuccess) {
+        this.wxMsgSuccess = wxMsgSuccess;
+    }
+
+    public Integer getWxMsgErrCount() {
+        return wxMsgErrCount;
+    }
+
+    public void setWxMsgErrCount(Integer wxMsgErrCount) {
+        this.wxMsgErrCount = wxMsgErrCount;
+    }
+
+    public String getWxMsgLastErr() {
+        return wxMsgLastErr;
+    }
+
+    public void setWxMsgLastErr(String wxMsgLastErr) {
+        this.wxMsgLastErr = wxMsgLastErr;
+    }
+
+    public Integer getState() {
+        return state;
+    }
+
+    public void setState(Integer state) {
+        this.state = state;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/message/entity/MessageType.java b/backend/src/main/java/com/rongyichuang/message/entity/MessageType.java
new file mode 100644
index 0000000..5c3fe24
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/message/entity/MessageType.java
@@ -0,0 +1,49 @@
+package com.rongyichuang.message.entity;
+
+/**
+ * 娑堟伅绫诲瀷鏋氫妇
+ */
+public enum MessageType {
+    /**
+     * 瀹℃牳閫氳繃
+     */
+    REVIEW_APPROVED(1, "瀹℃牳閫氳繃"),
+    
+    /**
+     * 瀹℃牳椹冲洖
+     */
+    REVIEW_REJECTED(2, "瀹℃牳椹冲洖"),
+    
+    /**
+     * 姣旇禌鏅嬬骇
+     */
+    COMPETITION_PROMOTED(3, "姣旇禌鏅嬬骇");
+    
+    private final int value;
+    private final String description;
+    
+    MessageType(int value, String description) {
+        this.value = value;
+        this.description = description;
+    }
+    
+    public int getValue() {
+        return value;
+    }
+    
+    public String getDescription() {
+        return description;
+    }
+    
+    /**
+     * 鏍规嵁鍊艰幏鍙栨灇涓�
+     */
+    public static MessageType fromValue(int value) {
+        for (MessageType type : MessageType.values()) {
+            if (type.getValue() == value) {
+                return type;
+            }
+        }
+        throw new IllegalArgumentException("Unknown MessageType value: " + value);
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/message/repository/MessageRepository.java b/backend/src/main/java/com/rongyichuang/message/repository/MessageRepository.java
new file mode 100644
index 0000000..17980b5
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/message/repository/MessageRepository.java
@@ -0,0 +1,53 @@
+package com.rongyichuang.message.repository;
+
+import com.rongyichuang.message.entity.Message;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * 娑堟伅Repository鎺ュ彛
+ */
+@Repository
+public interface MessageRepository extends JpaRepository<Message, Long> {
+
+    /**
+     * 鏍规嵁瀛﹀憳ID鏌ユ壘娑堟伅
+     */
+    List<Message> findByPlayerId(Integer playerId);
+
+    /**
+     * 鏍规嵁鐢ㄦ埛ID鏌ユ壘娑堟伅
+     */
+    List<Message> findByUserId(Integer userId);
+
+    /**
+     * 鏍规嵁鐩爣绫诲瀷鍜岀洰鏍嘔D鏌ユ壘娑堟伅
+     */
+    List<Message> findByTargetTypeAndTargetId(Integer targetType, Integer targetId);
+
+    /**
+     * 鏍规嵁鐘舵�佹煡鎵炬秷鎭�
+     */
+    List<Message> findByState(Integer state);
+
+    /**
+     * 鏍规嵁瀛﹀憳ID鍜岀姸鎬佹煡鎵炬秷鎭�
+     */
+    List<Message> findByPlayerIdAndState(Integer playerId, Integer state);
+
+    /**
+     * 鏌ユ壘寰俊娑堟伅鍙戦�佸け璐ョ殑娑堟伅
+     */
+    @Query("SELECT m FROM Message m WHERE m.wxMsgSuccess = false AND m.wxMsgErrCount < :maxErrCount")
+    List<Message> findFailedWxMessages(@Param("maxErrCount") Integer maxErrCount);
+
+    /**
+     * 鏍规嵁瀛﹀憳ID鏌ユ壘鏈彂甯冪殑娑堟伅
+     */
+    @Query("SELECT m FROM Message m WHERE m.playerId = :playerId AND m.state IN (0, 1)")
+    List<Message> findUnpublishedMessagesByPlayerId(@Param("playerId") Integer playerId);
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java b/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
index 1bbe844..adce16f 100644
--- a/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
+++ b/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
@@ -9,10 +9,16 @@
 import com.rongyichuang.player.dto.response.CurrentJudgeRatingResponse;
 import com.rongyichuang.player.dto.response.CurrentJudgeInfoResponse;
 import com.rongyichuang.player.dto.response.PlayerRegistrationResponse;
+import com.rongyichuang.player.dto.PromotionCompetitionResponse;
+import com.rongyichuang.player.dto.CompetitionParticipantResponse;
+import com.rongyichuang.player.dto.PromotionInput;
+import com.rongyichuang.player.dto.PromotionResult;
+import com.rongyichuang.player.dto.PromotableParticipantsResponse;
 import com.rongyichuang.player.service.PlayerApplicationService;
 import com.rongyichuang.player.service.ActivityPlayerDetailService;
 import com.rongyichuang.player.service.ActivityPlayerRatingService;
 import com.rongyichuang.player.service.ActivityPlayerService;
+import com.rongyichuang.player.service.PromotionService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.graphql.data.method.annotation.Argument;
@@ -32,25 +38,29 @@
     private final ActivityPlayerDetailService detailService;
     private final ActivityPlayerRatingService ratingService;
     private final ActivityPlayerService activityPlayerService;
+    private final PromotionService promotionService;
 
     public PlayerGraphqlApi(PlayerApplicationService service,
                            ActivityPlayerDetailService detailService,
                            ActivityPlayerRatingService ratingService,
-                           ActivityPlayerService activityPlayerService) {
+                           ActivityPlayerService activityPlayerService,
+                           PromotionService promotionService) {
         this.service = service;
         this.detailService = detailService;
         this.ratingService = ratingService;
         this.activityPlayerService = activityPlayerService;
+        this.promotionService = promotionService;
     }
 
     @QueryMapping
     public List<ActivityPlayerApplicationResponse> activityPlayerApplications(
             @Argument String name,
             @Argument Long activityId,
+            @Argument Integer state,
             @Argument Integer page,
             @Argument Integer size
     ) {
-        return service.listApplications(name, activityId, page, size);
+        return service.listApplications(name, activityId, state, page, size);
     }
 
     /**
@@ -142,4 +152,124 @@
             return response;
         }
     }
+
+    /**
+     * 瀹℃牳閫氳繃
+     */
+    @MutationMapping
+    public Boolean approveActivityPlayer(@Argument Long activityPlayerId, @Argument String feedback) {
+        log.info("瀹℃牳閫氳繃璇锋眰锛宎ctivityPlayerId: {}, feedback: {}", activityPlayerId, feedback);
+        try {
+            return activityPlayerService.approveActivityPlayer(activityPlayerId, feedback);
+        } catch (Exception e) {
+            log.error("瀹℃牳閫氳繃澶辫触: {}", e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * 瀹℃牳椹冲洖
+     */
+    @MutationMapping
+    public Boolean rejectActivityPlayer(@Argument Long activityPlayerId, @Argument String feedback) {
+        log.info("瀹℃牳椹冲洖璇锋眰锛宎ctivityPlayerId: {}, feedback: {}", activityPlayerId, feedback);
+        try {
+            return activityPlayerService.rejectActivityPlayer(activityPlayerId, feedback);
+        } catch (Exception e) {
+            log.error("瀹℃牳椹冲洖澶辫触: {}", e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * 鏇存柊瀹℃牳鎰忚
+     */
+    @MutationMapping
+    public Boolean updatePlayerFeedback(@Argument Long activityPlayerId, @Argument String feedback) {
+        log.info("鏇存柊瀹℃牳鎰忚璇锋眰锛宎ctivityPlayerId: {}, feedback: {}", activityPlayerId, feedback);
+        try {
+            return activityPlayerService.updateFeedback(activityPlayerId, feedback);
+        } catch (Exception e) {
+            log.error("鏇存柊瀹℃牳鎰忚澶辫触: {}", e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    @MutationMapping
+    public ActivityRegistrationResponse updateActivityRegistration(@Argument Long activityPlayerId, @Argument ActivityRegistrationInput input) {
+        try {
+            log.info("鏀跺埌鏇存柊鎶ュ悕璇锋眰锛屾姤鍚岻D: {}", activityPlayerId);
+            return activityPlayerService.updateActivityRegistration(activityPlayerId, input);
+        } catch (Exception e) {
+            log.error("鏇存柊鎶ュ悕澶辫触", e);
+            ActivityRegistrationResponse response = new ActivityRegistrationResponse();
+            response.setSuccess(false);
+            response.setMessage("鏇存柊鎶ュ悕澶辫触: " + e.getMessage());
+            return response;
+        }
+    }
+
+    /**
+     * 鑾峰彇姣旇禌鏅嬬骇鍒楄〃
+     */
+    @QueryMapping
+    public List<PromotionCompetitionResponse> promotionCompetitions(
+            @Argument String name,
+            @Argument Integer page,
+            @Argument Integer size) {
+        try {
+            log.info("鑾峰彇姣旇禌鏅嬬骇鍒楄〃锛屽悕绉拌繃婊�: {}, 椤电爜: {}, 椤靛ぇ灏�: {}", name, page, size);
+            return promotionService.getPromotionCompetitions(name, page, size);
+        } catch (Exception e) {
+            log.error("鑾峰彇姣旇禌鏅嬬骇鍒楄〃澶辫触: {}", e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * 鑾峰彇姣旇禌鍙傝禌浜哄憳
+     */
+    @QueryMapping
+    public List<CompetitionParticipantResponse> competitionParticipants(
+            @Argument Long competitionId,
+            @Argument Integer page,
+            @Argument Integer size) {
+        try {
+            log.info("鑾峰彇姣旇禌鍙傝禌浜哄憳锛屾瘮璧汭D: {}, 椤电爜: {}, 椤靛ぇ灏�: {}", competitionId, page, size);
+            return promotionService.getCompetitionParticipants(competitionId, page, size);
+        } catch (Exception e) {
+            log.error("鑾峰彇姣旇禌鍙傝禌浜哄憳澶辫触: {}", e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * 鑾峰彇鍙檵绾у弬璧涜�呭垪琛�
+     */
+    @QueryMapping
+    public PromotableParticipantsResponse promotableParticipants(@Argument Long currentStageId) {
+        try {
+            log.info("鑾峰彇鍙檵绾у弬璧涜�呭垪琛紝褰撳墠闃舵ID: {}", currentStageId);
+            return promotionService.getPromotableParticipants(currentStageId);
+        } catch (Exception e) {
+            log.error("鑾峰彇鍙檵绾у弬璧涜�呭垪琛ㄥけ璐�: {}", e.getMessage(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * 鎵ц鏅嬬骇鎿嶄綔
+     */
+    @MutationMapping
+    public PromotionResult promoteParticipants(@Argument PromotionInput input) {
+        try {
+            log.info("鎵ц鏅嬬骇鎿嶄綔锛屾瘮璧汭D: {}, 鍙傝禌鑰呮暟閲�: {}", 
+                input.getCompetitionId(), 
+                input.getParticipantIds() != null ? input.getParticipantIds().size() : 0);
+            return promotionService.promoteParticipants(input);
+        } catch (Exception e) {
+            log.error("鎵ц鏅嬬骇鎿嶄綔澶辫触: {}", e.getMessage(), e);
+            return PromotionResult.failure("鏅嬬骇鎿嶄綔澶辫触: " + e.getMessage());
+        }
+    }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/CompetitionParticipantResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/CompetitionParticipantResponse.java
new file mode 100644
index 0000000..741e1f7
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/dto/CompetitionParticipantResponse.java
@@ -0,0 +1,108 @@
+package com.rongyichuang.player.dto;
+
+import com.rongyichuang.player.entity.ActivityPlayer;
+
+import java.math.BigDecimal;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 姣旇禌鍙傝禌鑰呭搷搴旂被
+ */
+public class CompetitionParticipantResponse {
+    
+    private Long id;
+    private String playerName;
+    private String projectName;
+    private String phone;
+    private BigDecimal averageScore;
+    private Integer ratingCount;
+    private String applyTime;
+    private Integer state;
+    
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    
+    public CompetitionParticipantResponse() {}
+    
+    public CompetitionParticipantResponse(ActivityPlayer activityPlayer) {
+        this.id = activityPlayer.getId();
+        this.playerName = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getName() : "";
+        this.projectName = activityPlayer.getProjectName();
+        this.phone = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getPhone() : "";
+        this.averageScore = activityPlayer.getTotalScore();
+        this.ratingCount = 0; // 闇�瑕佷粠璇勫垎琛ㄤ腑缁熻
+        this.applyTime = activityPlayer.getCreateTime() != null ? 
+            activityPlayer.getCreateTime().format(FORMATTER) : null;
+        this.state = activityPlayer.getState();
+    }
+    
+    public CompetitionParticipantResponse(ActivityPlayer activityPlayer, Integer ratingCount) {
+        this(activityPlayer);
+        this.ratingCount = ratingCount != null ? ratingCount : 0;
+    }
+    
+    // Getters and Setters
+    
+    public Long getId() {
+        return id;
+    }
+    
+    public void setId(Long id) {
+        this.id = id;
+    }
+    
+    public String getPlayerName() {
+        return playerName;
+    }
+    
+    public void setPlayerName(String playerName) {
+        this.playerName = playerName;
+    }
+    
+    public String getProjectName() {
+        return projectName;
+    }
+    
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+    
+    public String getPhone() {
+        return phone;
+    }
+    
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+    
+    public BigDecimal getAverageScore() {
+        return averageScore;
+    }
+    
+    public void setAverageScore(BigDecimal averageScore) {
+        this.averageScore = averageScore;
+    }
+    
+    public Integer getRatingCount() {
+        return ratingCount;
+    }
+    
+    public void setRatingCount(Integer ratingCount) {
+        this.ratingCount = ratingCount;
+    }
+    
+    public String getApplyTime() {
+        return applyTime;
+    }
+    
+    public void setApplyTime(String applyTime) {
+        this.applyTime = applyTime;
+    }
+    
+    public Integer getState() {
+        return state;
+    }
+    
+    public void setState(Integer state) {
+        this.state = state;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantResponse.java
new file mode 100644
index 0000000..e59d920
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantResponse.java
@@ -0,0 +1,113 @@
+package com.rongyichuang.player.dto;
+
+import com.rongyichuang.player.entity.ActivityPlayer;
+
+import java.math.BigDecimal;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 鍙檵绾у弬璧涜�呭搷搴旂被
+ */
+public class PromotableParticipantResponse {
+    
+    private Long id;
+    private String playerName;
+    private String projectName;
+    private String phone;
+    private BigDecimal averageScore;
+    private Integer ratingCount;
+    private String applyTime;
+    private Integer state;
+    private Long playerId;
+    
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    
+    public PromotableParticipantResponse() {}
+    
+    public PromotableParticipantResponse(ActivityPlayer activityPlayer, BigDecimal averageScore, Integer ratingCount) {
+        this.id = activityPlayer.getId();
+        this.playerName = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getName() : "";
+        this.projectName = activityPlayer.getProjectName();
+        this.phone = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getPhone() : "";
+        this.averageScore = averageScore;
+        this.ratingCount = ratingCount != null ? ratingCount : 0;
+        this.applyTime = activityPlayer.getCreateTime() != null ? 
+            activityPlayer.getCreateTime().format(FORMATTER) : null;
+        this.state = activityPlayer.getState();
+        this.playerId = activityPlayer.getPlayerId();
+    }
+    
+    // Getters and Setters
+    
+    public Long getId() {
+        return id;
+    }
+    
+    public void setId(Long id) {
+        this.id = id;
+    }
+    
+    public String getPlayerName() {
+        return playerName;
+    }
+    
+    public void setPlayerName(String playerName) {
+        this.playerName = playerName;
+    }
+    
+    public String getProjectName() {
+        return projectName;
+    }
+    
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+    
+    public String getPhone() {
+        return phone;
+    }
+    
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+    
+    public BigDecimal getAverageScore() {
+        return averageScore;
+    }
+    
+    public void setAverageScore(BigDecimal averageScore) {
+        this.averageScore = averageScore;
+    }
+    
+    public Integer getRatingCount() {
+        return ratingCount;
+    }
+    
+    public void setRatingCount(Integer ratingCount) {
+        this.ratingCount = ratingCount;
+    }
+    
+    public String getApplyTime() {
+        return applyTime;
+    }
+    
+    public void setApplyTime(String applyTime) {
+        this.applyTime = applyTime;
+    }
+    
+    public Integer getState() {
+        return state;
+    }
+    
+    public void setState(Integer state) {
+        this.state = state;
+    }
+    
+    public Long getPlayerId() {
+        return playerId;
+    }
+    
+    public void setPlayerId(Long playerId) {
+        this.playerId = playerId;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantsResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantsResponse.java
new file mode 100644
index 0000000..6c05437
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantsResponse.java
@@ -0,0 +1,71 @@
+package com.rongyichuang.player.dto;
+
+import java.util.List;
+
+/**
+ * 鍙檵绾у弬璧涜�呭垪琛ㄥ搷搴旂被
+ */
+public class PromotableParticipantsResponse {
+    
+    private List<PromotableParticipantResponse> participants;
+    private Integer selectableCount;
+    private Integer totalCount;
+    private String previousStageName;
+    private String currentStageName;
+    
+    public PromotableParticipantsResponse() {}
+    
+    public PromotableParticipantsResponse(List<PromotableParticipantResponse> participants, 
+                                        Integer selectableCount, 
+                                        Integer totalCount,
+                                        String previousStageName,
+                                        String currentStageName) {
+        this.participants = participants;
+        this.selectableCount = selectableCount;
+        this.totalCount = totalCount;
+        this.previousStageName = previousStageName;
+        this.currentStageName = currentStageName;
+    }
+    
+    // Getters and Setters
+    
+    public List<PromotableParticipantResponse> getParticipants() {
+        return participants;
+    }
+    
+    public void setParticipants(List<PromotableParticipantResponse> participants) {
+        this.participants = participants;
+    }
+    
+    public Integer getSelectableCount() {
+        return selectableCount;
+    }
+    
+    public void setSelectableCount(Integer selectableCount) {
+        this.selectableCount = selectableCount;
+    }
+    
+    public Integer getTotalCount() {
+        return totalCount;
+    }
+    
+    public void setTotalCount(Integer totalCount) {
+        this.totalCount = totalCount;
+    }
+    
+    public String getPreviousStageName() {
+        return previousStageName;
+    }
+    
+    public void setPreviousStageName(String previousStageName) {
+        this.previousStageName = previousStageName;
+    }
+    
+    public String getCurrentStageName() {
+        return currentStageName;
+    }
+    
+    public void setCurrentStageName(String currentStageName) {
+        this.currentStageName = currentStageName;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/PromotionCompetitionResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/PromotionCompetitionResponse.java
new file mode 100644
index 0000000..d5521b4
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/dto/PromotionCompetitionResponse.java
@@ -0,0 +1,123 @@
+package com.rongyichuang.player.dto;
+
+import com.rongyichuang.activity.entity.Activity;
+
+import java.time.format.DateTimeFormatter;
+
+/**
+ * 姣旇禌鏅嬬骇鍒楄〃鍝嶅簲绫�
+ */
+public class PromotionCompetitionResponse {
+    
+    private Long id;
+    private String competitionName;
+    private String stageName;
+    private Integer maxParticipants;
+    private Integer currentCount;
+    private Integer status;
+    private String startTime;
+    private String endTime;
+    private Integer sortOrder;
+    private Integer state;
+    
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    
+    public PromotionCompetitionResponse() {}
+    
+    public PromotionCompetitionResponse(Activity competition, Activity stage, Integer currentCount) {
+        this.id = stage.getId();
+        this.competitionName = competition.getName();
+        this.stageName = stage.getName();
+        this.maxParticipants = stage.getPlayerMax();
+        this.currentCount = currentCount != null ? currentCount : 0;
+        this.status = stage.getState();
+        this.startTime = stage.getMatchTime() != null ? stage.getMatchTime().format(FORMATTER) : null;
+        this.endTime = stage.getSignupDeadline() != null ? stage.getSignupDeadline().format(FORMATTER) : null;
+        this.sortOrder = stage.getSortOrder();
+        this.state = stage.getState();
+    }
+    
+
+    
+    // Getters and Setters
+    
+    public Long getId() {
+        return id;
+    }
+    
+    public void setId(Long id) {
+        this.id = id;
+    }
+    
+    public String getCompetitionName() {
+        return competitionName;
+    }
+    
+    public void setCompetitionName(String competitionName) {
+        this.competitionName = competitionName;
+    }
+    
+    public String getStageName() {
+        return stageName;
+    }
+    
+    public void setStageName(String stageName) {
+        this.stageName = stageName;
+    }
+    
+    public Integer getMaxParticipants() {
+        return maxParticipants;
+    }
+    
+    public void setMaxParticipants(Integer maxParticipants) {
+        this.maxParticipants = maxParticipants;
+    }
+    
+    public Integer getCurrentCount() {
+        return currentCount;
+    }
+    
+    public void setCurrentCount(Integer currentCount) {
+        this.currentCount = currentCount;
+    }
+    
+    public Integer getStatus() {
+        return status;
+    }
+    
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+    
+    public String getStartTime() {
+        return startTime;
+    }
+    
+    public void setStartTime(String startTime) {
+        this.startTime = startTime;
+    }
+    
+    public String getEndTime() {
+        return endTime;
+    }
+    
+    public void setEndTime(String endTime) {
+        this.endTime = endTime;
+    }
+    
+    public Integer getSortOrder() {
+        return sortOrder;
+    }
+    
+    public void setSortOrder(Integer sortOrder) {
+        this.sortOrder = sortOrder;
+    }
+    
+    public Integer getState() {
+        return state;
+    }
+    
+    public void setState(Integer state) {
+        this.state = state;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/PromotionInput.java b/backend/src/main/java/com/rongyichuang/player/dto/PromotionInput.java
new file mode 100644
index 0000000..8094094
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/dto/PromotionInput.java
@@ -0,0 +1,47 @@
+package com.rongyichuang.player.dto;
+
+import java.util.List;
+
+/**
+ * 鏅嬬骇鎿嶄綔杈撳叆绫�
+ */
+public class PromotionInput {
+    
+    private Long competitionId;
+    private List<Long> participantIds;
+    private Long targetStageId;
+    
+    public PromotionInput() {}
+    
+    public PromotionInput(Long competitionId, List<Long> participantIds, Long targetStageId) {
+        this.competitionId = competitionId;
+        this.participantIds = participantIds;
+        this.targetStageId = targetStageId;
+    }
+    
+    // Getters and Setters
+    
+    public Long getCompetitionId() {
+        return competitionId;
+    }
+    
+    public void setCompetitionId(Long competitionId) {
+        this.competitionId = competitionId;
+    }
+    
+    public List<Long> getParticipantIds() {
+        return participantIds;
+    }
+    
+    public void setParticipantIds(List<Long> participantIds) {
+        this.participantIds = participantIds;
+    }
+    
+    public Long getTargetStageId() {
+        return targetStageId;
+    }
+    
+    public void setTargetStageId(Long targetStageId) {
+        this.targetStageId = targetStageId;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/PromotionResult.java b/backend/src/main/java/com/rongyichuang/player/dto/PromotionResult.java
new file mode 100644
index 0000000..1ef4759
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/dto/PromotionResult.java
@@ -0,0 +1,53 @@
+package com.rongyichuang.player.dto;
+
+/**
+ * 鏅嬬骇鎿嶄綔缁撴灉绫�
+ */
+public class PromotionResult {
+    
+    private Boolean success;
+    private String message;
+    private Integer promotedCount;
+    
+    public PromotionResult() {}
+    
+    public PromotionResult(Boolean success, String message, Integer promotedCount) {
+        this.success = success;
+        this.message = message;
+        this.promotedCount = promotedCount;
+    }
+    
+    public static PromotionResult success(String message, Integer promotedCount) {
+        return new PromotionResult(true, message, promotedCount);
+    }
+    
+    public static PromotionResult failure(String message) {
+        return new PromotionResult(false, message, 0);
+    }
+    
+    // Getters and Setters
+    
+    public Boolean getSuccess() {
+        return success;
+    }
+    
+    public void setSuccess(Boolean success) {
+        this.success = success;
+    }
+    
+    public String getMessage() {
+        return message;
+    }
+    
+    public void setMessage(String message) {
+        this.message = message;
+    }
+    
+    public Integer getPromotedCount() {
+        return promotedCount;
+    }
+    
+    public void setPromotedCount(Integer promotedCount) {
+        this.promotedCount = promotedCount;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java
index 4942a01..1651395 100644
--- a/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java
+++ b/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerApplicationResponse.java
@@ -4,6 +4,7 @@
     private Long id; // activity_player_id
     private String playerName;
     private String activityName;
+    private String projectName; // 椤圭洰鍚嶇О
     private String phone;
     private String applyTime; // ISO瀛楃涓�
     private Integer state;
@@ -17,6 +18,9 @@
     public String getActivityName() { return activityName; }
     public void setActivityName(String activityName) { this.activityName = activityName; }
 
+    public String getProjectName() { return projectName; }
+    public void setProjectName(String projectName) { this.projectName = projectName; }
+
     public String getPhone() { return phone; }
     public void setPhone(String phone) { this.phone = phone; }
 
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerDetailResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerDetailResponse.java
index 890437e..f06a247 100644
--- a/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerDetailResponse.java
+++ b/backend/src/main/java/com/rongyichuang/player/dto/response/ActivityPlayerDetailResponse.java
@@ -10,7 +10,10 @@
     private PlayerInfoResponse playerInfo; // 瀛﹀憳淇℃伅
     private RegionInfoResponse regionInfo; // 鍖哄煙淇℃伅
     private String activityName; // 姣旇禌鍚嶇О
+    private String projectName; // 椤圭洰鍚嶇О
     private String description; // 鍙傝禌椤圭洰绠�浠�
+    private String feedback; // 瀹℃牳鎰忚
+    private Integer state; // 瀹℃牳鐘舵�侊細0=鏈鏍革紝1=閫氳繃锛�2=椹冲洖
     private List<SubmissionMediaResponse> submissionFiles; // 鎻愪氦鐨勮祫鏂�
     private RatingFormResponse ratingForm; // 璇勫垎琛ㄥ崟
 
@@ -30,9 +33,18 @@
     public String getActivityName() { return activityName; }
     public void setActivityName(String activityName) { this.activityName = activityName; }
 
+    public String getProjectName() { return projectName; }
+    public void setProjectName(String projectName) { this.projectName = projectName; }
+
     public String getDescription() { return description; }
     public void setDescription(String description) { this.description = description; }
 
+    public String getFeedback() { return feedback; }
+    public void setFeedback(String feedback) { this.feedback = feedback; }
+
+    public Integer getState() { return state; }
+    public void setState(Integer state) { this.state = state; }
+
     public List<SubmissionMediaResponse> getSubmissionFiles() { return submissionFiles; }
     public void setSubmissionFiles(List<SubmissionMediaResponse> submissionFiles) { this.submissionFiles = submissionFiles; }
 
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/response/JudgeRatingStatusResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/response/JudgeRatingStatusResponse.java
index 0782aa9..dac2ebe 100644
--- a/backend/src/main/java/com/rongyichuang/player/dto/response/JudgeRatingStatusResponse.java
+++ b/backend/src/main/java/com/rongyichuang/player/dto/response/JudgeRatingStatusResponse.java
@@ -9,19 +9,19 @@
     
     private Long judgeId;
     private String judgeName;
+    private Boolean hasRated;
+    private String ratingTime;
     private BigDecimal totalScore;
-    private Integer status;
-    private Boolean isCurrentJudge;
 
     public JudgeRatingStatusResponse() {}
 
-    public JudgeRatingStatusResponse(Long judgeId, String judgeName, BigDecimal totalScore, 
-                                   Integer status, Boolean isCurrentJudge) {
+    public JudgeRatingStatusResponse(Long judgeId, String judgeName, Boolean hasRated, 
+                                   String ratingTime, BigDecimal totalScore) {
         this.judgeId = judgeId;
         this.judgeName = judgeName;
+        this.hasRated = hasRated;
+        this.ratingTime = ratingTime;
         this.totalScore = totalScore;
-        this.status = status;
-        this.isCurrentJudge = isCurrentJudge;
     }
 
     public Long getJudgeId() {
@@ -40,27 +40,27 @@
         this.judgeName = judgeName;
     }
 
+    public Boolean getHasRated() {
+        return hasRated;
+    }
+
+    public void setHasRated(Boolean hasRated) {
+        this.hasRated = hasRated;
+    }
+
+    public String getRatingTime() {
+        return ratingTime;
+    }
+
+    public void setRatingTime(String ratingTime) {
+        this.ratingTime = ratingTime;
+    }
+
     public BigDecimal getTotalScore() {
         return totalScore;
     }
 
     public void setTotalScore(BigDecimal totalScore) {
         this.totalScore = totalScore;
-    }
-
-    public Integer getStatus() {
-        return status;
-    }
-
-    public void setStatus(Integer status) {
-        this.status = status;
-    }
-
-    public Boolean getIsCurrentJudge() {
-        return isCurrentJudge;
-    }
-
-    public void setIsCurrentJudge(Boolean isCurrentJudge) {
-        this.isCurrentJudge = isCurrentJudge;
     }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/response/PlayerInfoResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/response/PlayerInfoResponse.java
index ea7ee50..cb88e4a 100644
--- a/backend/src/main/java/com/rongyichuang/player/dto/response/PlayerInfoResponse.java
+++ b/backend/src/main/java/com/rongyichuang/player/dto/response/PlayerInfoResponse.java
@@ -1,5 +1,7 @@
 package com.rongyichuang.player.dto.response;
 
+import com.rongyichuang.common.dto.response.MediaResponse;
+
 /**
  * 瀛﹀憳淇℃伅鍝嶅簲
  */
@@ -7,8 +9,13 @@
     private Long id;
     private String name;
     private String phone;
+    private Integer gender; // 鎬у埆
+    private String birthday; // 鍑虹敓鏃ユ湡
+    private String education; // 瀛﹀巻
+    private String introduction; // 涓汉浠嬬粛
     private String description; // 绠�浠�
-    private String avatarUrl; // 澶村儚URL锛堜粠 t_media 鑾峰彇锛屼娇鐢� MediaTargetType.STUDENT_AVATAR锛屽�间负6锛�
+    private String avatarUrl; // 澶村儚URL锛堜粠 t_media 鑾峰彇锛屼娇鐢� MediaTargetType.USER_AVATAR锛屽�间负7锛�
+    private MediaResponse avatar; // 澶村儚Media瀵硅薄
 
     // Constructors
     public PlayerInfoResponse() {}
@@ -23,9 +30,24 @@
     public String getPhone() { return phone; }
     public void setPhone(String phone) { this.phone = phone; }
 
+    public Integer getGender() { return gender; }
+    public void setGender(Integer gender) { this.gender = gender; }
+
+    public String getBirthday() { return birthday; }
+    public void setBirthday(String birthday) { this.birthday = birthday; }
+
+    public String getEducation() { return education; }
+    public void setEducation(String education) { this.education = education; }
+
+    public String getIntroduction() { return introduction; }
+    public void setIntroduction(String introduction) { this.introduction = introduction; }
+
     public String getDescription() { return description; }
     public void setDescription(String description) { this.description = description; }
 
     public String getAvatarUrl() { return avatarUrl; }
     public void setAvatarUrl(String avatarUrl) { this.avatarUrl = avatarUrl; }
+
+    public MediaResponse getAvatar() { return avatar; }
+    public void setAvatar(MediaResponse avatar) { this.avatar = avatar; }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/dto/response/SubmissionMediaResponse.java b/backend/src/main/java/com/rongyichuang/player/dto/response/SubmissionMediaResponse.java
index d387fde..d534642 100644
--- a/backend/src/main/java/com/rongyichuang/player/dto/response/SubmissionMediaResponse.java
+++ b/backend/src/main/java/com/rongyichuang/player/dto/response/SubmissionMediaResponse.java
@@ -10,6 +10,7 @@
     private String fileExt; // 鏂囦欢鎵╁睍鍚�
     private Long fileSize; // 鏂囦欢澶у皬
     private Integer mediaType; // 濯掍綋绫诲瀷锛�1=鍥剧墖锛�2=瑙嗛锛�3=鏂囨。绛夛級
+    private String thumbUrl; // 缂╃暐鍥綰RL锛堣棰戝皝闈㈠浘锛�
 
     // Constructors
     public SubmissionMediaResponse() {}
@@ -32,4 +33,7 @@
 
     public Integer getMediaType() { return mediaType; }
     public void setMediaType(Integer mediaType) { this.mediaType = mediaType; }
+
+    public String getThumbUrl() { return thumbUrl; }
+    public void setThumbUrl(String thumbUrl) { this.thumbUrl = thumbUrl; }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/entity/ActivityPlayer.java b/backend/src/main/java/com/rongyichuang/player/entity/ActivityPlayer.java
index 6d8f241..7cc8f7b 100644
--- a/backend/src/main/java/com/rongyichuang/player/entity/ActivityPlayer.java
+++ b/backend/src/main/java/com/rongyichuang/player/entity/ActivityPlayer.java
@@ -13,7 +13,6 @@
  */
 @Entity
 @Table(name = "t_activity_player")
-@Where(clause = "state = 1")
 public class ActivityPlayer extends BaseEntity {
 
     /**
@@ -83,10 +82,10 @@
     private Integer rank;
 
     /**
-     * 鐘舵�侊細1-姝e父锛�0-鍒犻櫎
+     * 鐘舵�侊細0-鏈鏍革紝1-瀹℃牳閫氳繃锛�2-瀹℃牳椹冲洖
      */
     @Column(name = "state", nullable = false)
-    private Integer state = 1;
+    private Integer state = 0;
 
     // JPA鍏宠仈鍏崇郴
     /**
diff --git a/backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java b/backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java
index 588707e..cd84782 100644
--- a/backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java
+++ b/backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java
@@ -90,7 +90,26 @@
     List<ActivityPlayer> findTopRankedPlayers(@Param("activityId") Long activityId);
 
     /**
-     * 鏍规嵁鐘舵�佺粺璁℃姤鍚嶆暟閲�
+     * 缁熻鎸囧畾鐘舵�佺殑鍙傝禌閫夋墜鏁伴噺
      */
     long countByState(Integer state);
+
+    /**
+     * 鏍规嵁闃舵ID鍜岀姸鎬佺粺璁″弬璧涢�夋墜鏁伴噺
+     */
+    Long countByStageIdAndState(Long stageId, Integer state);
+
+    /**
+     * 鏍规嵁闃舵ID鍜岀姸鎬佹煡鎵惧弬璧涢�夋墜锛堝寘鍚�夋墜淇℃伅锛�
+     */
+    @Query("SELECT ap FROM ActivityPlayer ap " +
+           "LEFT JOIN FETCH ap.player p " +
+           "WHERE ap.stageId = :stageId AND ap.state = :state " +
+           "ORDER BY ap.createTime DESC")
+    List<ActivityPlayer> findByStageIdAndStateWithPlayerOrderByCreateTimeDesc(@Param("stageId") Long stageId, @Param("state") Integer state);
+
+    /**
+     * 妫�鏌ラ�夋墜鏄惁宸插湪鎸囧畾闃舵鎶ュ悕
+     */
+    boolean existsByStageIdAndPlayerId(Long stageId, Long playerId);
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java
index 0eb0337..5f56ef0 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java
@@ -8,14 +8,20 @@
 import com.rongyichuang.common.entity.Media;
 import com.rongyichuang.common.enums.MediaTargetType;
 import com.rongyichuang.common.repository.MediaRepository;
+import com.rongyichuang.common.dto.response.MediaResponse;
 import jakarta.persistence.EntityManager;
 import jakarta.persistence.PersistenceContext;
+import jakarta.persistence.Query;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
 import java.util.stream.Collectors;
 
 /**
@@ -31,6 +37,9 @@
 
     private final MediaRepository mediaRepository;
     private final RatingSchemeRepository ratingSchemeRepository;
+    
+    @Value("${app.media-url}")
+    private String mediaBaseUrl;
 
     public ActivityPlayerDetailService(MediaRepository mediaRepository, 
                                      RatingSchemeRepository ratingSchemeRepository) {
@@ -48,60 +57,141 @@
 
         // 鏌ヨ鍩烘湰淇℃伅锛屽寘鍚尯鍩熶俊鎭�
         String sql = """
-            SELECT ap.id, ap.description, ap.activity_id, ap.region_id,
+            SELECT ap.id as ap_id, ap.description as ap_description, ap.activity_id, ap.region_id,
+                   ap.project_name, ap.feedback, ap.state as ap_state,
                    p.id as player_id, p.name as player_name, p.phone, p.description as player_desc,
+                   p.gender, u.birthday, p.education, p.introduction, u.id as user_id,
                    a.name as activity_name, a.rating_scheme_id,
-                   r.id as r_id, r.name as region_name, r.full_path as region_path
+                   r.id as region_id, r.name as region_name, r.full_path as region_path
             FROM t_activity_player ap
             JOIN t_player p ON p.id = ap.player_id
+            JOIN t_user u ON u.id = p.user_id
             JOIN t_activity a ON a.id = ap.activity_id
             LEFT JOIN t_region r ON r.id = ap.region_id
             WHERE ap.id = ?
             """;
 
         @SuppressWarnings("unchecked")
-        List<Object[]> results = em.createNativeQuery(sql)
-                .setParameter(1, activityPlayerId)
-                .getResultList();
+        Query query = em.createNativeQuery(sql);
+        query.setParameter(1, activityPlayerId);
+        
+        // 浣跨敤 Tuple 鏉ヨ幏鍙栧懡鍚嶅瓧娈�
+        query.unwrap(org.hibernate.query.NativeQuery.class).setTupleTransformer(
+            (tuple, aliases) -> {
+                Map<String, Object> result = new HashMap<>();
+                for (int i = 0; i < aliases.length; i++) {
+                    result.put(aliases[i], tuple[i]);
+                }
+                return result;
+            }
+        );
+        
+        @SuppressWarnings("unchecked")
+        List<Map<String, Object>> results = query.getResultList();
 
         if (results.isEmpty()) {
             log.warn("鏈壘鍒版姤鍚嶈褰曪紝activityPlayerId: {}", activityPlayerId);
             return null;
         }
 
-        Object[] row = results.get(0);
+        Map<String, Object> row = results.get(0);
         ActivityPlayerDetailResponse response = new ActivityPlayerDetailResponse();
         response.setId(activityPlayerId);
-        response.setDescription(row[1] != null ? row[1].toString() : "");
-        response.setActivityName(row[8] != null ? row[8].toString() : "");
+        response.setDescription(row.get("ap_description") != null ? row.get("ap_description").toString() : "");
+        response.setProjectName(row.get("project_name") != null ? row.get("project_name").toString() : "");
+        response.setFeedback(row.get("feedback") != null ? row.get("feedback").toString() : "");
+        
+        Object stateObj = row.get("ap_state");
+        if (stateObj != null) {
+            if (stateObj instanceof Number) {
+                response.setState(((Number) stateObj).intValue());
+            } else {
+                log.warn("鐘舵�佺被鍨嬩笉鍖归厤: {}, 绫诲瀷: {}", stateObj, stateObj.getClass().getName());
+                response.setState(Integer.valueOf(stateObj.toString()));
+            }
+        } else {
+            response.setState(0);
+        }
+        response.setActivityName(row.get("activity_name") != null ? row.get("activity_name").toString() : "");
 
         // 鏋勫缓瀛﹀憳淇℃伅
         PlayerInfoResponse playerInfo = new PlayerInfoResponse();
-        playerInfo.setId(row[4] != null ? ((Number) row[4]).longValue() : null);
-        playerInfo.setName(row[5] != null ? row[5].toString() : "");
-        playerInfo.setPhone(row[6] != null ? row[6].toString() : "");
-        playerInfo.setDescription(row[7] != null ? row[7].toString() : "");
+        Object playerIdObj = row.get("player_id");
+        if (playerIdObj != null) {
+            if (playerIdObj instanceof Number) {
+                playerInfo.setId(((Number) playerIdObj).longValue());
+            } else {
+                log.warn("瀛﹀憳ID绫诲瀷涓嶅尮閰�: {}, 绫诲瀷: {}", playerIdObj, playerIdObj.getClass().getName());
+                playerInfo.setId(Long.valueOf(playerIdObj.toString()));
+            }
+        } else {
+            playerInfo.setId(null);
+        }
+        playerInfo.setName(row.get("player_name") != null ? row.get("player_name").toString() : "");
+        playerInfo.setPhone(row.get("phone") != null ? row.get("phone").toString() : "");
+        playerInfo.setDescription(row.get("player_desc") != null ? row.get("player_desc").toString() : "");
+        
+        Object genderObj = row.get("gender");
+        if (genderObj != null) {
+            if (genderObj instanceof Number) {
+                playerInfo.setGender(((Number) genderObj).intValue());
+            } else {
+                log.warn("鎬у埆绫诲瀷涓嶅尮閰�: {}, 绫诲瀷: {}", genderObj, genderObj.getClass().getName());
+                playerInfo.setGender(Integer.valueOf(genderObj.toString()));
+            }
+        } else {
+            playerInfo.setGender(null);
+        }
+        
+        Object birthdayObj = row.get("birthday");
+        playerInfo.setBirthday(birthdayObj != null ? 
+            (birthdayObj instanceof java.sql.Date ? ((java.sql.Date) birthdayObj).toString() : birthdayObj.toString()) : null);
+        playerInfo.setEducation(row.get("education") != null ? row.get("education").toString() : "");
+        playerInfo.setIntroduction(row.get("introduction") != null ? row.get("introduction").toString() : "");
 
         // 鏋勫缓鍖哄煙淇℃伅
-        RegionInfoResponse regionInfo = new RegionInfoResponse();
-        if (row[10] != null) {
-            regionInfo.setId(((Number) row[10]).longValue());
-            regionInfo.setName(row[11] != null ? row[11].toString() : "");
-            regionInfo.setFullPath(row[12] != null ? row[12].toString() : "");
+        Object regionIdObj = row.get("region_id");
+        if (regionIdObj != null) {
+            RegionInfoResponse regionInfo = new RegionInfoResponse();
+            if (regionIdObj instanceof Number) {
+                regionInfo.setId(((Number) regionIdObj).longValue());
+            } else {
+                log.warn("鍖哄煙ID绫诲瀷涓嶅尮閰�: {}, 绫诲瀷: {}", regionIdObj, regionIdObj.getClass().getName());
+                regionInfo.setId(Long.valueOf(regionIdObj.toString()));
+            }
+            regionInfo.setName(row.get("region_name") != null ? row.get("region_name").toString() : "");
+            regionInfo.setFullPath(row.get("region_path") != null ? row.get("region_path").toString() : "");
             response.setRegionInfo(regionInfo);
             log.info("鎵惧埌鍖哄煙淇℃伅: {}", regionInfo.getName());
         }
 
-        // 鏌ヨ瀛﹀憳澶村儚锛堜娇鐢ㄦ灇涓惧父閲忚〃绀哄鍛樺ご鍍忕被鍨嬶級
-        if (playerInfo.getId() != null) {
+        // 鏌ヨ鐢ㄦ埛澶村儚锛堜娇鐢║SER_AVATAR鍏宠仈鍒扮敤鎴凤級
+        Object userIdObj = row.get("user_id");
+        log.info("璋冭瘯锛氫粠鏌ヨ缁撴灉涓幏鍙栫殑user_id: {}", userIdObj);
+        if (userIdObj != null) {
+            Long userId;
+            if (userIdObj instanceof Number) {
+                userId = ((Number) userIdObj).longValue();
+            } else {
+                userId = Long.valueOf(userIdObj.toString());
+            }
+            log.info("璋冭瘯锛氳В鏋愬悗鐨剈serId: {}", userId);
+            
             List<Media> avatarMedias = mediaRepository.findByTargetTypeAndTargetIdAndState(
-                MediaTargetType.STUDENT_AVATAR.getValue(), playerInfo.getId(), 1);
+                MediaTargetType.USER_AVATAR.getValue(), userId, 1);
+            log.info("璋冭瘯锛氭煡璇㈠埌鐨勫ご鍍忓獟浣撴暟閲�: {}", avatarMedias.size());
             if (!avatarMedias.isEmpty()) {
                 Media avatar = avatarMedias.get(0);
-                String avatarUrl = avatar.getPath();
+                String avatarUrl = buildFullMediaUrl(avatar.getPath());
                 playerInfo.setAvatarUrl(avatarUrl);
-                log.info("鎵惧埌瀛﹀憳澶村儚: {}", avatarUrl);
+                // 璁剧疆avatar瀵硅薄
+                playerInfo.setAvatar(convertToMediaResponse(avatar));
+                log.info("鎵惧埌鐢ㄦ埛澶村儚: {}", avatarUrl);
+            } else {
+                log.info("璋冭瘯锛氭湭鎵惧埌鐢ㄦ埛澶村儚锛寀serId: {}, targetType: {}", userId, MediaTargetType.USER_AVATAR.getValue());
             }
+        } else {
+            log.warn("璋冭瘯锛歶ser_id涓簄ull");
         }
         response.setPlayerInfo(playerInfo);
 
@@ -115,7 +205,8 @@
         log.info("鎵惧埌鎻愪氦璧勬枡 {} 涓�", submissionFiles.size());
 
         // 鏌ヨ璇勫垎妯℃澘
-        Long ratingSchemeId = row[9] != null ? ((Number) row[9]).longValue() : null;
+        Object ratingSchemeIdObj = row.get("rating_scheme_id");
+        Long ratingSchemeId = ratingSchemeIdObj != null ? ((Number) ratingSchemeIdObj).longValue() : null;
         if (ratingSchemeId != null) {
             RatingFormResponse ratingForm = buildRatingForm(ratingSchemeId);
             response.setRatingForm(ratingForm);
@@ -131,10 +222,11 @@
         SubmissionMediaResponse response = new SubmissionMediaResponse();
         response.setId(media.getId());
         response.setName(media.getName());
-        response.setUrl(media.getPath());
+        response.setUrl(buildFullMediaUrl(media.getPath()));
         response.setFileExt(media.getFileExt());
         response.setFileSize(media.getFileSize() != null ? media.getFileSize().longValue() : null);
         response.setMediaType(media.getMediaType());
+        response.setThumbUrl(buildFullMediaUrl(media.getThumbPath()));
         return response;
     }
 
@@ -168,4 +260,49 @@
 
         return response;
     }
+
+    /**
+     * 杞崲濯掍綋鏂囦欢涓篗ediaResponse
+     */
+    private MediaResponse convertToMediaResponse(Media media) {
+        MediaResponse response = new MediaResponse();
+        response.setId(media.getId());
+        response.setName(media.getName());
+        response.setPath(media.getPath());
+        response.setFileSize(media.getFileSize());
+        response.setFileExt(media.getFileExt());
+        response.setThumbPath(media.getThumbPath());
+        response.setDuration(media.getDuration());
+        response.setDescription(media.getDescription());
+        response.setTargetType(media.getTargetType());
+        response.setTargetId(media.getTargetId());
+        response.setMediaType(media.getMediaType());
+        
+        // 璁剧疆瀹屾暣URL
+        response.setFullUrl(buildFullMediaUrl(media.getPath()));
+        response.setFullThumbUrl(buildFullMediaUrl(media.getThumbPath()));
+        
+        return response;
+    }
+    
+    private String buildFullMediaUrl(String path) {
+        if (!StringUtils.hasText(path)) {
+            return null;
+        }
+        
+        // 濡傛灉宸茬粡鏄畬鏁碪RL锛岀洿鎺ヨ繑鍥�
+        if (path.startsWith("http://") || path.startsWith("https://")) {
+            return path;
+        }
+        
+        // 濡傛灉娌℃湁閰嶇疆mediaBaseUrl锛岃繑鍥炲師璺緞
+        if (!StringUtils.hasText(mediaBaseUrl)) {
+            return path;
+        }
+        
+        // 鎷兼帴瀹屾暣URL
+        String baseUrl = mediaBaseUrl.endsWith("/") ? mediaBaseUrl : mediaBaseUrl + "/";
+        String relativePath = path.startsWith("/") ? path.substring(1) : path;
+        return baseUrl + relativePath;
+    }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java
index e26369e..6726e7c 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerRatingService.java
@@ -263,18 +263,20 @@
             Optional<ActivityPlayerRating> ratingOpt = activityPlayerRatingRepository
                     .findByActivityPlayerIdAndJudgeId(activityPlayerId, judgeId);
             
+            Boolean hasRated = false;
+            String ratingTime = null;
             BigDecimal totalScore = null;
-            Integer status = 0;
             
             if (ratingOpt.isPresent()) {
                 ActivityPlayerRating rating = ratingOpt.get();
+                hasRated = rating.getState() != null && rating.getState() == 1; // 浣跨敤state鍒ゆ柇鏄惁宸茶瘎鍒�
                 totalScore = rating.getTotalScore();
-                status = rating.getState();
+                if (rating.getUpdateTime() != null) {
+                    ratingTime = rating.getUpdateTime().toString();
+                }
             }
             
-            Boolean isCurrentJudge = judgeId.equals(currentJudgeId);
-            
-            return new JudgeRatingStatusResponse(judgeId, judgeName, totalScore, status, isCurrentJudge);
+            return new JudgeRatingStatusResponse(judgeId, judgeName, hasRated, ratingTime, totalScore);
         }).collect(java.util.stream.Collectors.toList());
     }
 
diff --git a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerService.java b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerService.java
index 85464e6..9c2a940 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerService.java
@@ -8,6 +8,8 @@
 import com.rongyichuang.player.entity.Player;
 import com.rongyichuang.player.repository.ActivityPlayerRepository;
 import com.rongyichuang.player.repository.PlayerRepository;
+import com.rongyichuang.activity.repository.ActivityRepository;
+import com.rongyichuang.activity.entity.Activity;
 import com.rongyichuang.common.entity.Media;
 import com.rongyichuang.common.repository.MediaRepository;
 import com.rongyichuang.common.enums.MediaTargetType;
@@ -40,6 +42,9 @@
 
     @Autowired
     private PlayerRepository playerRepository;
+
+    @Autowired
+    private ActivityRepository activityRepository;
 
     @Autowired
     private MediaRepository mediaRepository;
@@ -99,11 +104,24 @@
             }
             log.info("鏈彂鐜伴噸澶嶆姤鍚�");
 
-            // 4. 鍒涘缓鎶ュ悕璁板綍
+            // 4. 鏌ユ壘绗竴闃舵锛屽鏋滄病鏈夊垯浣跨敤娲诲姩鏈韩
+            log.info("鏌ユ壘娲诲姩鐨勭涓�闃舵锛屾椿鍔↖D: {}", input.getActivityId());
+            Activity firstStage = activityRepository.findFirstStageByActivityId(input.getActivityId());
+            Long stageId;
+            if (firstStage != null) {
+                stageId = firstStage.getId();
+                log.info("鎵惧埌绗竴闃舵锛岄樁娈礗D: {}, 闃舵鍚嶇О: {}", firstStage.getId(), firstStage.getName());
+            } else {
+                // 濡傛灉娌℃湁鎵惧埌绗竴闃舵锛屼娇鐢ㄦ椿鍔ㄦ湰韬綔涓洪樁娈�
+                stageId = input.getActivityId();
+                log.info("鏈壘鍒扮涓�闃舵锛屼娇鐢ㄦ椿鍔ㄦ湰韬綔涓洪樁娈碉紝娲诲姩ID: {}", input.getActivityId());
+            }
+
+            // 5. 鍒涘缓鎶ュ悕璁板綍
             log.info("寮�濮嬪垱寤烘姤鍚嶈褰�");
             ActivityPlayer activityPlayer = new ActivityPlayer();
             activityPlayer.setActivityId(input.getActivityId());
-            activityPlayer.setStageId(input.getActivityId()); // 鏍规嵁鏂囨。锛氬鏋滄瘮璧涙湭瀹氫箟闃舵锛宻tage_id璁句负activity_id
+            activityPlayer.setStageId(stageId); // 缁戝畾鍒扮涓�闃舵鎴栨椿鍔ㄦ湰韬�
             activityPlayer.setPlayerId(player.getId());
             activityPlayer.setRegionId(input.getRegionId());
             activityPlayer.setProjectName(input.getProjectName()); // 璁剧疆椤圭洰鍚嶇О
@@ -114,18 +132,19 @@
             ActivityPlayer savedActivityPlayer = activityPlayerRepository.save(activityPlayer);
             log.info("鎶ュ悕璁板綍鍒涘缓鎴愬姛锛孖D: {}", savedActivityPlayer.getId());
 
-            // 5. 淇濆瓨鍏朵粬濯掍綋鏂囦欢锛堝吋瀹规棫鐗堟湰锛�
+            // 6. 淇濆瓨鍏朵粬濯掍綋鏂囦欢锛堝吋瀹规棫鐗堟湰锛�
             if (input.getMediaFiles() != null && !input.getMediaFiles().isEmpty()) {
                 saveMediaFiles(savedActivityPlayer.getId(), input.getMediaFiles());
             }
 
-            // 6. 淇濆瓨澶村儚濯掍綋璁板綍
+            // 7. 淇濆瓨澶村儚濯掍綋璁板綍
             if (input.getPlayerInfo().getAvatarMediaId() != null && !input.getPlayerInfo().getAvatarMediaId().trim().isEmpty()) {
                 savePlayerAvatarMedia(player.getId(), input.getPlayerInfo().getAvatarMediaId());
             }
 
-            // 7. 淇濆瓨闄勪欢濯掍綋璁板綍
+            // 8. 淇濆瓨闄勪欢濯掍綋璁板綍
             if (input.getAttachmentMediaIds() != null && !input.getAttachmentMediaIds().isEmpty()) {
+                log.info("寮�濮嬩繚瀛橀檮浠跺獟浣撹褰曪紝鎶ュ悕ID: {}, 闄勪欢鏁伴噺: {}", savedActivityPlayer.getId(), input.getAttachmentMediaIds().size());
                 saveAttachmentMediaRecords(savedActivityPlayer.getId(), input.getAttachmentMediaIds());
             }
 
@@ -329,26 +348,234 @@
     }
 
     /**
-     * 淇濆瓨瀛﹀憳澶村儚
-     * 鍙傝�僯udge妯″潡鐨勫疄鐜帮紝灏嗗凡涓婁紶鐨勫ご鍍忓獟浣撴枃浠跺叧鑱斿埌瀛﹀憳
+     * 瀹℃牳閫氳繃
+     */
+    public Boolean approveActivityPlayer(Long activityPlayerId, String feedback) {
+        try {
+            Optional<ActivityPlayer> activityPlayerOpt = activityPlayerRepository.findById(activityPlayerId);
+            if (!activityPlayerOpt.isPresent()) {
+                throw new RuntimeException("鎶ュ悕璁板綍涓嶅瓨鍦�");
+            }
+
+            ActivityPlayer activityPlayer = activityPlayerOpt.get();
+            activityPlayer.setState(1); // 1=瀹℃牳閫氳繃
+            activityPlayer.setFeedback(feedback);
+            activityPlayerRepository.save(activityPlayer);
+
+            log.info("瀹℃牳閫氳繃鎴愬姛锛宎ctivityPlayerId: {}", activityPlayerId);
+            return true;
+        } catch (Exception e) {
+            log.error("瀹℃牳閫氳繃澶辫触锛宎ctivityPlayerId: {}", activityPlayerId, e);
+            throw new RuntimeException("瀹℃牳閫氳繃澶辫触", e);
+        }
+    }
+
+    /**
+     * 瀹℃牳椹冲洖
+     */
+    public Boolean rejectActivityPlayer(Long activityPlayerId, String feedback) {
+        try {
+            Optional<ActivityPlayer> activityPlayerOpt = activityPlayerRepository.findById(activityPlayerId);
+            if (!activityPlayerOpt.isPresent()) {
+                throw new RuntimeException("鎶ュ悕璁板綍涓嶅瓨鍦�");
+            }
+
+            ActivityPlayer activityPlayer = activityPlayerOpt.get();
+            activityPlayer.setState(2); // 2=瀹℃牳椹冲洖
+            activityPlayer.setFeedback(feedback);
+            activityPlayerRepository.save(activityPlayer);
+
+            log.info("瀹℃牳椹冲洖鎴愬姛锛宎ctivityPlayerId: {}", activityPlayerId);
+            return true;
+        } catch (Exception e) {
+            log.error("瀹℃牳椹冲洖澶辫触锛宎ctivityPlayerId: {}", activityPlayerId, e);
+            throw new RuntimeException("瀹℃牳椹冲洖澶辫触", e);
+        }
+    }
+
+    /**
+     * 鏇存柊鎶ュ悕淇℃伅
+     */
+    public ActivityRegistrationResponse updateActivityRegistration(Long activityPlayerId, ActivityRegistrationInput input) {
+        try {
+            log.info("寮�濮嬫洿鏂版姤鍚嶄俊鎭紝鎶ュ悕ID: {}", activityPlayerId);
+            
+            // 1. 鏌ユ壘鐜版湁鎶ュ悕璁板綍
+            Optional<ActivityPlayer> activityPlayerOpt = activityPlayerRepository.findById(activityPlayerId);
+            if (!activityPlayerOpt.isPresent()) {
+                throw new RuntimeException("鎶ュ悕璁板綍涓嶅瓨鍦�");
+            }
+            
+            ActivityPlayer activityPlayer = activityPlayerOpt.get();
+            
+            // 2. 鏇存柊閫夋墜淇℃伅
+            Player player = playerRepository.findById(activityPlayer.getPlayerId()).orElse(null);
+            if (player != null) {
+                player.setName(input.getPlayerInfo().getName());
+                if (input.getPlayerInfo().getGender() != null) {
+                    player.setGender(input.getPlayerInfo().getGender());
+                }
+                if (input.getPlayerInfo().getEducation() != null) {
+                    player.setEducation(input.getPlayerInfo().getEducation());
+                }
+                if (input.getPlayerInfo().getIntroduction() != null) {
+                    player.setIntroduction(input.getPlayerInfo().getIntroduction());
+                }
+                if (input.getPlayerInfo().getDescription() != null) {
+                    player.setDescription(input.getPlayerInfo().getDescription());
+                }
+                playerRepository.save(player);
+                
+                // 鏇存柊鐢ㄦ埛淇℃伅
+                updateUserInfo(player, input);
+            }
+            
+            // 3. 鏇存柊鎶ュ悕璁板綍鍩烘湰淇℃伅
+            if (input.getProjectName() != null) {
+                activityPlayer.setProjectName(input.getProjectName());
+            }
+            if (input.getDescription() != null) {
+                activityPlayer.setDescription(input.getDescription());
+            }
+            if (input.getRegionId() != null) {
+                activityPlayer.setRegionId(input.getRegionId());
+            }
+            
+            activityPlayerRepository.save(activityPlayer);
+            
+            // 4. 澶勭悊澶村儚鏇存柊
+            if (input.getPlayerInfo().getAvatarMediaId() != null && !input.getPlayerInfo().getAvatarMediaId().isEmpty()) {
+                // 淇濆瓨鏂板ご鍍�
+                savePlayerAvatarMedia(player.getId(), input.getPlayerInfo().getAvatarMediaId());
+            }
+            
+            // 5. 澶勭悊姣旇禌鍥剧墖鍜岃棰戞洿鏂�
+            if (input.getMediaFiles() != null) {
+                // 鍒犻櫎鏃х殑姣旇禌鍥剧墖鍜岃棰戣褰�
+                deleteOldSubmissionMedia(activityPlayerId);
+                // 淇濆瓨鏂扮殑姣旇禌鍥剧墖鍜岃棰�
+                saveMediaFiles(activityPlayerId, input.getMediaFiles());
+            }
+            
+            // 6. 澶勭悊闄勪欢鏇存柊
+            if (input.getAttachmentMediaIds() != null) {
+                // 鍒犻櫎鏃ч檮浠惰褰�
+                deleteOldAttachmentMedia(activityPlayerId);
+                // 淇濆瓨鏂伴檮浠�
+                saveAttachmentMediaRecords(activityPlayerId, input.getAttachmentMediaIds());
+            }
+            
+            log.info("鏇存柊鎶ュ悕淇℃伅鎴愬姛锛屾姤鍚岻D: {}", activityPlayerId);
+            
+            ActivityRegistrationResponse response = new ActivityRegistrationResponse();
+            response.setSuccess(true);
+            response.setMessage("鏇存柊鎶ュ悕淇℃伅鎴愬姛");
+            response.setRegistrationId(activityPlayerId);
+            response.setPlayerId(player.getId());
+            response.setUserId(player.getUserId());
+            response.setActivityPlayerId(activityPlayerId);
+            
+            return response;
+            
+        } catch (Exception e) {
+            log.error("鏇存柊鎶ュ悕淇℃伅澶辫触锛屾姤鍚岻D: {}", activityPlayerId, e);
+            throw new RuntimeException("鏇存柊鎶ュ悕淇℃伅澶辫触: " + e.getMessage(), e);
+        }
+    }
+
+
+
+    /**
+     * 鍒犻櫎鏃х殑姣旇禌鍥剧墖鍜岃棰戣褰�
+     */
+    private void deleteOldSubmissionMedia(Long activityPlayerId) {
+        try {
+            List<Media> oldSubmissionMedia = mediaRepository.findByTargetTypeAndTargetIdAndState(
+                MediaTargetType.ACTIVITY_PLAYER_SUBMISSION.getValue(), activityPlayerId, 1);
+            for (Media media : oldSubmissionMedia) {
+                media.setState(0); // 璁剧疆涓哄垹闄ょ姸鎬�
+                mediaRepository.save(media);
+            }
+            log.info("鍒犻櫎鏃ф瘮璧涘浘鐗�/瑙嗛璁板綍鎴愬姛锛屾姤鍚岻D: {}, 鍒犻櫎鏁伴噺: {}", activityPlayerId, oldSubmissionMedia.size());
+        } catch (Exception e) {
+            log.error("鍒犻櫎鏃ф瘮璧涘浘鐗�/瑙嗛璁板綍澶辫触锛屾姤鍚岻D: {}", activityPlayerId, e);
+        }
+    }
+
+    /**
+     * 鍒犻櫎鏃х殑闄勪欢濯掍綋璁板綍
+     */
+    private void deleteOldAttachmentMedia(Long activityPlayerId) {
+        try {
+            List<Media> oldAttachments = mediaRepository.findByTargetTypeAndTargetIdAndState(
+                5, activityPlayerId, 1); // target_type=5 鏄檮浠�
+            for (Media oldAttachment : oldAttachments) {
+                oldAttachment.setState(0); // 璁剧疆涓哄垹闄ょ姸鎬�
+                mediaRepository.save(oldAttachment);
+            }
+            log.info("鍒犻櫎鏃ч檮浠惰褰曟垚鍔燂紝鎶ュ悕ID: {}, 鍒犻櫎鏁伴噺: {}", activityPlayerId, oldAttachments.size());
+        } catch (Exception e) {
+            log.error("鍒犻櫎鏃ч檮浠惰褰曞け璐ワ紝鎶ュ悕ID: {}", activityPlayerId, e);
+        }
+    }
+
+    /**
+     * 鏇存柊瀹℃牳鎰忚
+     */
+    public Boolean updateFeedback(Long activityPlayerId, String feedback) {
+        try {
+            Optional<ActivityPlayer> activityPlayerOpt = activityPlayerRepository.findById(activityPlayerId);
+            if (!activityPlayerOpt.isPresent()) {
+                throw new RuntimeException("鎶ュ悕璁板綍涓嶅瓨鍦�");
+            }
+
+            ActivityPlayer activityPlayer = activityPlayerOpt.get();
+            activityPlayer.setFeedback(feedback);
+            activityPlayerRepository.save(activityPlayer);
+
+            log.info("鏇存柊瀹℃牳鎰忚鎴愬姛锛宎ctivityPlayerId: {}", activityPlayerId);
+            return true;
+        } catch (Exception e) {
+            log.error("鏇存柊瀹℃牳鎰忚澶辫触锛宎ctivityPlayerId: {}", activityPlayerId, e);
+            throw new RuntimeException("鏇存柊瀹℃牳鎰忚澶辫触", e);
+        }
+    }
+
+    /**
+     * 淇濆瓨鐢ㄦ埛澶村儚
+     * 灏嗗凡涓婁紶鐨勫ご鍍忓獟浣撴枃浠跺叧鑱斿埌鐢ㄦ埛
      */
     private void savePlayerAvatar(Long playerId, Long avatarMediaId) {
         try {
-            // 鏌ユ壘鐜版湁鐨勫ご鍍忚褰曞苟鍒犻櫎锛堢‘淇濅竴涓鍛樺彧鏈変竴涓ご鍍忥級
-            mediaRepository.deleteByTargetTypeAndTargetId(MediaTargetType.STUDENT_AVATAR.getValue(), playerId);
+            // 鑾峰彇player瀵瑰簲鐨勭敤鎴稩D
+            Optional<Player> playerOpt = playerRepository.findById(playerId);
+            if (!playerOpt.isPresent()) {
+                log.warn("鏈壘鍒板鍛樿褰曪紝瀛﹀憳ID: {}", playerId);
+                return;
+            }
+            
+            Player player = playerOpt.get();
+            Long userId = player.getUserId();
+            if (userId == null) {
+                log.warn("瀛﹀憳鏈叧鑱旂敤鎴凤紝瀛﹀憳ID: {}", playerId);
+                return;
+            }
+            
+            // 鏌ユ壘鐜版湁鐨勫ご鍍忚褰曞苟鍒犻櫎锛堢‘淇濅竴涓敤鎴峰彧鏈変竴涓ご鍍忥級
+            mediaRepository.deleteByTargetTypeAndTargetId(MediaTargetType.USER_AVATAR.getValue(), userId);
             
             // 鏇存柊濯掍綋鏂囦欢鐨則arget淇℃伅
             Media avatarMedia = mediaRepository.findById(avatarMediaId).orElse(null);
             if (avatarMedia != null) {
-                avatarMedia.setTargetType(MediaTargetType.STUDENT_AVATAR.getValue());
-                avatarMedia.setTargetId(playerId);
+                avatarMedia.setTargetType(MediaTargetType.USER_AVATAR.getValue());
+                avatarMedia.setTargetId(userId);
                 mediaRepository.save(avatarMedia);
-                log.info("瀛﹀憳澶村儚淇濆瓨鎴愬姛锛屽鍛業D: {}, 濯掍綋ID: {}", playerId, avatarMediaId);
+                log.info("鐢ㄦ埛澶村儚淇濆瓨鎴愬姛锛岀敤鎴稩D: {}, 濯掍綋ID: {}", userId, avatarMediaId);
             } else {
                 log.warn("鏈壘鍒板ご鍍忓獟浣撴枃浠讹紝濯掍綋ID: {}", avatarMediaId);
             }
         } catch (Exception e) {
-            log.error("淇濆瓨瀛﹀憳澶村儚鏃跺彂鐢熼敊璇紝瀛﹀憳ID: {}, 濯掍綋ID: {}", playerId, avatarMediaId, e);
+            log.error("淇濆瓨鐢ㄦ埛澶村儚鏃跺彂鐢熼敊璇紝瀛﹀憳ID: {}, 濯掍綋ID: {}", playerId, avatarMediaId, e);
         }
     }
 
@@ -385,7 +612,7 @@
             MediaSaveInput input = new MediaSaveInput();
             input.setTargetType("player");
             input.setTargetId(playerId);
-            input.setUrl(avatarMediaId); // COS璺緞
+            input.setPath(avatarMediaId); // COS璺緞
             input.setFileName("avatar.jpg"); // 榛樿澶村儚鏂囦欢鍚�
             input.setFileExt("jpg"); // 鏂囦欢鎵╁睍鍚�
             input.setFileSize(0L); // 鏂囦欢澶у皬鏆傛椂璁句负0
@@ -411,7 +638,7 @@
                 MediaSaveInput input = new MediaSaveInput();
                 input.setTargetType("activity_player");
                 input.setTargetId(activityPlayerId);
-                input.setUrl(mediaId); // COS璺緞
+                input.setPath(mediaId); // COS璺緞
                 input.setFileName("attachment"); // 榛樿闄勪欢鏂囦欢鍚�
                 input.setFileExt("jpg"); // 鏂囦欢鎵╁睍鍚�
                 input.setFileSize(0L); // 鏂囦欢澶у皬鏆傛椂璁句负0
diff --git a/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java b/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java
index 962cf57..f78ffc1 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java
@@ -18,9 +18,9 @@
      * 鏌ヨ娲诲姩鎶ュ悕淇℃伅
      */
     @SuppressWarnings("unchecked")
-    public List<ActivityPlayerApplicationResponse> listApplications(String name, Long activityId, Integer page, Integer size) {
+    public List<ActivityPlayerApplicationResponse> listApplications(String name, Long activityId, Integer state, Integer page, Integer size) {
         String baseSql =
-            "SELECT ap.id, p.name AS player_name, a.name AS activity_name, p.phone AS phone, ap.create_time AS apply_time, p.state AS state " +
+            "SELECT ap.id, p.name AS player_name, a.name AS activity_name, ap.project_name AS project_name, p.phone AS phone, ap.create_time AS apply_time, ap.state AS state " +
             "FROM t_activity_player ap " +
             "JOIN t_player p ON p.id = ap.player_id " +
             "JOIN t_activity a ON a.id = ap.activity_id ";
@@ -37,7 +37,15 @@
             if (hasCondition) {
                 whereClause.append(" AND ");
             }
-            whereClause.append("ap.activity_id = :activityId");
+            whereClause.append("ap.stage_id = :activityId");
+            hasCondition = true;
+        }
+        
+        if (state != null) {
+            if (hasCondition) {
+                whereClause.append(" AND ");
+            }
+            whereClause.append("ap.state = :state");
             hasCondition = true;
         }
         
@@ -56,6 +64,9 @@
         if (activityId != null) {
             q.setParameter("activityId", activityId);
         }
+        if (state != null) {
+            q.setParameter("state", state);
+        }
         List<Object[]> rows = q.getResultList();
         List<ActivityPlayerApplicationResponse> list = new ArrayList<>();
         for (Object[] r : rows) {
@@ -63,10 +74,11 @@
             dto.setId(r[0] != null ? Long.valueOf(r[0].toString()) : null); // activity_player_id
             dto.setPlayerName(r[1] != null ? r[1].toString() : "");
             dto.setActivityName(r[2] != null ? r[2].toString() : "");
-            dto.setPhone(r[3] != null ? r[3].toString() : "");
-            dto.setApplyTime(r[4] != null ? r[4].toString() : "");
-            // 鏄犲皠鐘舵�侊細浣跨敤 t_player.state锛�1=鏈夋晥锛�0=鏃犳晥锛�
-            dto.setState(r[5] != null ? Integer.valueOf(r[5].toString()) : 1);
+            dto.setProjectName(r[3] != null ? r[3].toString() : ""); // project_name
+            dto.setPhone(r[4] != null ? r[4].toString() : "");
+            dto.setApplyTime(r[5] != null ? r[5].toString() : "");
+            // 鏄犲皠鐘舵�侊細浣跨敤 t_activity_player.state锛�0=鏈鏍革紝1=瀹℃牳閫氳繃锛�2=瀹℃牳椹冲洖锛�
+            dto.setState(r[6] != null ? Integer.valueOf(r[6].toString()) : 0);
             list.add(dto);
         }
         return list;
diff --git a/backend/src/main/java/com/rongyichuang/player/service/PromotionService.java b/backend/src/main/java/com/rongyichuang/player/service/PromotionService.java
new file mode 100644
index 0000000..f34c9ac
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/player/service/PromotionService.java
@@ -0,0 +1,328 @@
+package com.rongyichuang.player.service;
+
+import com.rongyichuang.activity.entity.Activity;
+import com.rongyichuang.activity.entity.ActivityPlayerRating;
+import com.rongyichuang.activity.repository.ActivityRepository;
+import com.rongyichuang.activity.repository.ActivityPlayerRatingRepository;
+import com.rongyichuang.common.entity.Media;
+import com.rongyichuang.common.repository.MediaRepository;
+import com.rongyichuang.player.dto.*;
+import com.rongyichuang.player.entity.ActivityPlayer;
+import com.rongyichuang.player.repository.ActivityPlayerRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 姣旇禌鏅嬬骇鏈嶅姟绫�
+ */
+@Service
+public class PromotionService {
+    
+    @Autowired
+    private ActivityRepository activityRepository;
+    
+    @Autowired
+    private ActivityPlayerRepository activityPlayerRepository;
+    
+    @Autowired
+    private ActivityPlayerRatingRepository activityPlayerRatingRepository;
+    
+    @Autowired
+    private MediaRepository mediaRepository;
+    
+    /**
+     * 鑾峰彇姣旇禌鏅嬬骇鍒楄〃
+     */
+    public List<PromotionCompetitionResponse> getPromotionCompetitions(String name, Integer page, Integer size) {
+        List<PromotionCompetitionResponse> result = new ArrayList<>();
+        
+        // 鏌ヨ鎵�鏈夋湁鏁堢殑涓绘瘮璧涳紙pid = 0锛�
+        List<Activity> competitions = activityRepository.findByPidAndStateOrderByCreateTimeAsc(0L, 1);
+        
+        // 濡傛灉鏈夊悕绉拌繃婊ゆ潯浠讹紝杩涜杩囨护
+        if (name != null && !name.trim().isEmpty()) {
+            competitions = competitions.stream()
+                .filter(comp -> comp.getName().contains(name.trim()))
+                .collect(Collectors.toList());
+        }
+        
+        // 涓烘瘡涓瘮璧涙煡璇㈠叾闃舵
+        for (Activity competition : competitions) {
+            List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(competition.getId(), 1);
+            
+            for (Activity stage : stages) {
+                // 缁熻褰撳墠闃舵鐨勫弬璧涗汉鏁�
+                Long playerCountLong = activityPlayerRepository.countByStageIdAndState(stage.getId(), 1);
+                Integer currentCount = playerCountLong != null ? playerCountLong.intValue() : 0;
+                
+                PromotionCompetitionResponse response = new PromotionCompetitionResponse(competition, stage, currentCount);
+                result.add(response);
+            }
+        }
+        
+        // 绠�鍗曞垎椤靛鐞嗭紙瀹為檯椤圭洰涓缓璁娇鐢ㄦ暟鎹簱鍒嗛〉锛�
+        if (page != null && size != null && page > 0 && size > 0) {
+            int start = (page - 1) * size;
+            int end = Math.min(start + size, result.size());
+            if (start < result.size()) {
+                result = result.subList(start, end);
+            } else {
+                result = new ArrayList<>();
+            }
+        }
+        
+        return result;
+    }
+    
+    /**
+     * 鑾峰彇姣旇禌鍙傝禌浜哄憳
+     */
+    public List<CompetitionParticipantResponse> getCompetitionParticipants(Long competitionId, Integer page, Integer size) {
+        // 鏌ヨ鎸囧畾闃舵鐨勬墍鏈夊弬璧涜�咃紙宸插鏍搁�氳繃鐨勶級
+        List<ActivityPlayer> participants = activityPlayerRepository.findByStageIdAndStateWithPlayerOrderByCreateTimeDesc(competitionId, 1);
+        
+        // 杞崲涓哄搷搴斿璞�
+        List<CompetitionParticipantResponse> result = participants.stream()
+            .map(participant -> {
+                // 杩欓噷鍙互娣诲姞璇勫垎娆℃暟鐨勭粺璁¢�昏緫
+                Integer ratingCount = 0; // TODO: 浠庤瘎鍒嗚〃涓粺璁�
+                return new CompetitionParticipantResponse(participant, ratingCount);
+            })
+            .collect(Collectors.toList());
+        
+        // 绠�鍗曞垎椤靛鐞�
+        if (page != null && size != null && page > 0 && size > 0) {
+            int start = (page - 1) * size;
+            int end = Math.min(start + size, result.size());
+            if (start < result.size()) {
+                result = result.subList(start, end);
+            } else {
+                result = new ArrayList<>();
+            }
+        }
+        
+        return result;
+    }
+    
+    /**
+     * 鑾峰彇鍙檵绾х殑鍙傝禌鑰呭垪琛�
+     */
+    public PromotableParticipantsResponse getPromotableParticipants(Long currentStageId) {
+        try {
+            // 鏌ヨ褰撳墠闃舵淇℃伅
+            Activity currentStage = activityRepository.findById(currentStageId).orElse(null);
+            if (currentStage == null) {
+                return new PromotableParticipantsResponse(new ArrayList<>(), 0, 0, "", "");
+            }
+            
+            // 鏌ユ壘涓婁竴涓樁娈�
+            Activity previousStage = findPreviousStage(currentStage);
+            if (previousStage == null) {
+                return new PromotableParticipantsResponse(new ArrayList<>(), 0, 0, "", currentStage.getName());
+            }
+            
+            // 鏌ヨ涓婁竴闃舵鐨勬墍鏈夋湁鏁堝弬璧涜��
+            List<ActivityPlayer> previousParticipants = activityPlayerRepository
+                .findByStageIdAndStateWithPlayerOrderByCreateTimeDesc(previousStage.getId(), 1);
+            
+            // 鏌ヨ褰撳墠闃舵宸叉湁鐨勫弬璧涜�匢D鍒楄〃
+            List<ActivityPlayer> currentParticipants = activityPlayerRepository
+                .findByStageIdAndStateWithPlayerOrderByCreateTimeDesc(currentStageId, 1);
+            List<Long> currentPlayerIds = currentParticipants.stream()
+                .map(ActivityPlayer::getPlayerId)
+                .collect(Collectors.toList());
+            
+            // 杩囨护鎺夊凡缁忓湪褰撳墠闃舵鐨勫弬璧涜�咃紝骞惰绠楀钩鍧囧垎
+            List<PromotableParticipantResponse> promotableList = new ArrayList<>();
+            
+            for (ActivityPlayer participant : previousParticipants) {
+                // 鎺掗櫎宸茬粡鍦ㄥ綋鍓嶉樁娈电殑鍙傝禌鑰�
+                if (!currentPlayerIds.contains(participant.getPlayerId())) {
+                    // 璁$畻骞冲潎鍒�
+                    BigDecimal averageScore = calculateAverageScore(participant.getId());
+                    
+                    // 鍙寘鍚湁璇勫垎鐨勫弬璧涜��
+                    if (averageScore != null && averageScore.compareTo(BigDecimal.ZERO) > 0) {
+                        // 缁熻璇勫垎娆℃暟
+                        long ratingCount = activityPlayerRatingRepository
+                            .countCompletedRatingsByActivityPlayerId(participant.getId());
+                        
+                        PromotableParticipantResponse response = new PromotableParticipantResponse(
+                            participant, averageScore, (int) ratingCount);
+                        promotableList.add(response);
+                    }
+                }
+            }
+            
+            // 鎸夊钩鍧囧垎闄嶅簭鎺掑簭
+            promotableList.sort((a, b) -> {
+                if (a.getAverageScore() == null && b.getAverageScore() == null) return 0;
+                if (a.getAverageScore() == null) return 1;
+                if (b.getAverageScore() == null) return -1;
+                return b.getAverageScore().compareTo(a.getAverageScore());
+            });
+            
+            // 璁$畻鍙�夋嫨浜烘暟锛堝綋鍓嶉樁娈垫渶澶т汉鏁� - 褰撳墠闃舵宸叉湁浜烘暟锛�
+            Integer maxParticipants = currentStage.getPlayerMax() != null ? currentStage.getPlayerMax() : 0;
+            Integer selectableCount = Math.max(0, maxParticipants - currentParticipants.size());
+            
+            return new PromotableParticipantsResponse(
+                promotableList, 
+                selectableCount, 
+                promotableList.size(),
+                previousStage.getName(),
+                currentStage.getName()
+            );
+            
+        } catch (Exception e) {
+            return new PromotableParticipantsResponse(new ArrayList<>(), 0, 0, "", "");
+        }
+    }
+    
+    /**
+     * 璁$畻鍙傝禌鑰呯殑骞冲潎鍒�
+     */
+    private BigDecimal calculateAverageScore(Long activityPlayerId) {
+        List<ActivityPlayerRating> ratings = activityPlayerRatingRepository
+            .findCompletedRatingsByActivityPlayerId(activityPlayerId);
+        
+        if (ratings.isEmpty()) {
+            return null;
+        }
+        
+        BigDecimal totalScore = BigDecimal.ZERO;
+        int count = 0;
+        
+        for (ActivityPlayerRating rating : ratings) {
+            if (rating.getTotalScore() != null) {
+                totalScore = totalScore.add(rating.getTotalScore());
+                count++;
+            }
+        }
+        
+        if (count == 0) {
+            return null;
+        }
+        
+        return totalScore.divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP);
+    }
+    
+    /**
+     * 鏌ユ壘涓婁竴涓樁娈�
+     */
+    private Activity findPreviousStage(Activity currentStage) {
+        // 鏌ヨ鍚屼竴姣旇禌涓嬬殑鎵�鏈夐樁娈碉紝鎸夋帓搴忛『搴�
+        List<Activity> stages = activityRepository.findByPidAndStateOrderBySortOrderAsc(currentStage.getPid(), 1);
+        
+        // 鎵惧埌褰撳墠闃舵鐨勪綅缃紝杩斿洖涓婁竴涓樁娈�
+        for (int i = 0; i < stages.size(); i++) {
+            if (stages.get(i).getId().equals(currentStage.getId()) && i > 0) {
+                return stages.get(i - 1);
+            }
+        }
+        
+        return null; // 娌℃湁涓婁竴涓樁娈�
+    }
+    
+    /**
+     * 鎵ц鏅嬬骇鎿嶄綔
+     */
+    @Transactional
+    public PromotionResult promoteParticipants(PromotionInput input) {
+        try {
+            if (input.getParticipantIds() == null || input.getParticipantIds().isEmpty()) {
+                return PromotionResult.failure("璇烽�夋嫨瑕佹檵绾х殑鍙傝禌鑰�");
+            }
+            
+            // 鏌ヨ鐩爣鏅嬬骇闃舵淇℃伅锛堢敤鎴风偣鍑荤殑闃舵灏辨槸瑕佹檵绾у埌鐨勯樁娈碉級
+            Activity targetStage = activityRepository.findById(input.getCompetitionId()).orElse(null);
+            if (targetStage == null) {
+                return PromotionResult.failure("鐩爣鏅嬬骇闃舵涓嶅瓨鍦�");
+            }
+            
+            // 鏌ヨ瑕佹檵绾х殑鍙傝禌鑰�
+            List<ActivityPlayer> participants = activityPlayerRepository.findAllById(input.getParticipantIds());
+            if (participants.isEmpty()) {
+                return PromotionResult.failure("娌℃湁鎵惧埌瑕佹檵绾х殑鍙傝禌鑰�");
+            }
+            
+            int promotedCount = 0;
+            
+            // 涓烘瘡涓弬璧涜�呭垱寤虹洰鏍囬樁娈电殑鎶ュ悕璁板綍
+            for (ActivityPlayer participant : participants) {
+                // 妫�鏌ユ槸鍚﹀凡缁忓湪鐩爣闃舵鎶ュ悕
+                boolean alreadyPromoted = activityPlayerRepository.existsByStageIdAndPlayerId(targetStage.getId(), participant.getPlayerId());
+                if (!alreadyPromoted) {
+                    // 鍒涘缓鏂扮殑鎶ュ悕璁板綍
+                    ActivityPlayer newParticipant = new ActivityPlayer();
+                    newParticipant.setActivityId(targetStage.getPid()); // 涓绘瘮璧汭D
+                    newParticipant.setStageId(targetStage.getId()); // 鐩爣闃舵ID
+                    newParticipant.setPlayerId(participant.getPlayerId());
+                    newParticipant.setRegionId(participant.getRegionId());
+                    newParticipant.setProjectName(participant.getProjectName());
+                    newParticipant.setDescription(participant.getDescription());
+                    newParticipant.setState(1); // 鐩存帴璁句负瀹℃牳閫氳繃
+                    
+                    ActivityPlayer savedParticipant = activityPlayerRepository.save(newParticipant);
+                    
+                    // 澶嶅埗濯掍綋鏂囦欢
+                    copyMediaFiles(participant.getId(), savedParticipant.getId());
+                    
+                    promotedCount++;
+                }
+            }
+            
+            return PromotionResult.success(
+                String.format("鎴愬姛鏅嬬骇 %d 鍚嶅弬璧涜�呭埌 %s", promotedCount, targetStage.getName()),
+                promotedCount
+            );
+            
+        } catch (Exception e) {
+            return PromotionResult.failure("鏅嬬骇鎿嶄綔澶辫触锛�" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 澶嶅埗濯掍綋鏂囦欢
+     */
+    private void copyMediaFiles(Long sourceActivityPlayerId, Long targetActivityPlayerId) {
+        try {
+            // 鑾峰彇婧愬弬璧涜�呯殑鎵�鏈夊獟浣撴枃浠� (targetType=5 琛ㄧず ACTIVITY_PLAYER)
+            List<Media> sourceMediaFiles = mediaRepository.findByTargetTypeAndTargetIdAndState(5, sourceActivityPlayerId, 1);
+            
+            for (Media sourceMedia : sourceMediaFiles) {
+                // 鍒涘缓鏂扮殑濯掍綋璁板綍
+                Media newMedia = new Media();
+                newMedia.setName(sourceMedia.getName());
+                newMedia.setPath(sourceMedia.getPath());
+                newMedia.setFileSize(sourceMedia.getFileSize());
+                newMedia.setFileExt(sourceMedia.getFileExt());
+                newMedia.setMediaType(sourceMedia.getMediaType());
+                newMedia.setTargetType(5); // ACTIVITY_PLAYER
+                newMedia.setTargetId(targetActivityPlayerId);
+                newMedia.setThumbPath(sourceMedia.getThumbPath());
+                newMedia.setDuration(sourceMedia.getDuration());
+                newMedia.setDescription(sourceMedia.getDescription());
+                
+                // 淇濆瓨鏂扮殑濯掍綋璁板綍
+                mediaRepository.save(newMedia);
+            }
+        } catch (Exception e) {
+            // 璁板綍閿欒浣嗕笉涓柇鏅嬬骇娴佺▼
+            System.err.println("澶嶅埗濯掍綋鏂囦欢澶辫触: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 鏌ユ壘涓嬩竴涓樁娈�
+     */
+
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/rating/dto/response/RatingSchemeResponse.java b/backend/src/main/java/com/rongyichuang/rating/dto/response/RatingSchemeResponse.java
index fe90677..c5aebb8 100644
--- a/backend/src/main/java/com/rongyichuang/rating/dto/response/RatingSchemeResponse.java
+++ b/backend/src/main/java/com/rongyichuang/rating/dto/response/RatingSchemeResponse.java
@@ -15,6 +15,8 @@
     private String name;
     private String description;
     private Integer totalScore;
+    private Integer state;
+    private String stateName;
     private List<RatingItemResponse> items;
     private LocalDateTime createTime;
     private LocalDateTime updateTime;
@@ -27,6 +29,8 @@
         this.name = scheme.getName();
         this.description = scheme.getDescription();
         this.totalScore = scheme.getTotalScore();
+        this.state = scheme.getState();
+        this.stateName = getStateNameByValue(scheme.getState());
         this.createTime = scheme.getCreateTime();
         this.updateTime = scheme.getUpdateTime();
         
@@ -93,4 +97,32 @@
     public void setUpdateTime(LocalDateTime updateTime) {
         this.updateTime = updateTime;
     }
+
+    public Integer getState() {
+        return state;
+    }
+
+    public void setState(Integer state) {
+        this.state = state;
+    }
+
+    public String getStateName() {
+        return stateName;
+    }
+
+    public void setStateName(String stateName) {
+        this.stateName = stateName;
+    }
+
+    /**
+     * 鏍规嵁鐘舵�佸�艰幏鍙栫姸鎬佸悕绉�
+     */
+    private String getStateNameByValue(Integer state) {
+        if (state == null) return "鏈煡";
+        switch (state) {
+            case 1: return "姝e父";
+            case 0: return "宸插垹闄�";
+            default: return "鏈煡";
+        }
+    }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/rating/entity/RatingItem.java b/backend/src/main/java/com/rongyichuang/rating/entity/RatingItem.java
index 40eb649..a6f3673 100644
--- a/backend/src/main/java/com/rongyichuang/rating/entity/RatingItem.java
+++ b/backend/src/main/java/com/rongyichuang/rating/entity/RatingItem.java
@@ -59,6 +59,14 @@
     }
 
     // Getter鍜孲etter鏂规硶
+    public Long getSchemeId() {
+        return schemeId;
+    }
+    
+    public void setSchemeId(Long schemeId) {
+        this.schemeId = schemeId;
+    }
+    
     public RatingScheme getScheme() {
         return scheme;
     }
diff --git a/backend/src/main/java/com/rongyichuang/rating/service/RatingSchemeService.java b/backend/src/main/java/com/rongyichuang/rating/service/RatingSchemeService.java
index 12131a9..9bc3347 100644
--- a/backend/src/main/java/com/rongyichuang/rating/service/RatingSchemeService.java
+++ b/backend/src/main/java/com/rongyichuang/rating/service/RatingSchemeService.java
@@ -139,6 +139,7 @@
                 // 鏂板鏉$洰
                 item = new RatingItem();
                 item.setScheme(scheme);
+                item.setSchemeId(scheme.getId());
             } else {
                 // 淇敼鏉$洰
                 item = existingItems.stream()
@@ -147,6 +148,7 @@
                         .orElse(new RatingItem());
                 if (item.getScheme() == null) {
                     item.setScheme(scheme);
+                    item.setSchemeId(scheme.getId());
                 }
             }
             
diff --git a/backend/src/main/resources/graphql/activity.graphqls b/backend/src/main/resources/graphql/activity.graphqls
index 78e63c3..d6eeb5b 100644
--- a/backend/src/main/resources/graphql/activity.graphqls
+++ b/backend/src/main/resources/graphql/activity.graphqls
@@ -13,6 +13,7 @@
     ratingSchemeId: ID!
     playerMax: Int
     state: Int!
+    sortOrder: Int
     createTime: String!
     updateTime: String!
     
@@ -75,6 +76,7 @@
     ratingSchemeId: ID
     playerMax: Int
     state: Int
+    sortOrder: Int
 }
 
 # 姣旇禌璇勫杈撳叆绫诲瀷
diff --git a/backend/src/main/resources/graphql/media.graphqls b/backend/src/main/resources/graphql/media.graphqls
index 3b73a15..8964117 100644
--- a/backend/src/main/resources/graphql/media.graphqls
+++ b/backend/src/main/resources/graphql/media.graphqls
@@ -11,10 +11,10 @@
     saveMediaV2(input: MediaSaveInput!): MediaSaveResponse!
     
     "淇濆瓨閫夋墜澶村儚"
-    savePlayerAvatar(playerId: ID!, url: String!, fileName: String, fileSize: Long): MediaSaveResponse!
+    savePlayerAvatar(playerId: ID!, path: String!, fileName: String, fileSize: Long): MediaSaveResponse!
     
     "淇濆瓨娲诲姩鎶ュ悕闄勪欢"
-    saveActivityPlayerAttachment(activityPlayerId: ID!, url: String!, fileName: String, fileSize: Long, mediaType: Int!): MediaSaveResponse!
+    saveActivityPlayerAttachment(activityPlayerId: ID!, path: String!, fileName: String, fileSize: Long, mediaType: Int!): MediaSaveResponse!
 }
 
 extend type Query {
@@ -94,8 +94,8 @@
 input MediaSaveInput {
     targetType: String!     # 鐩爣绫诲瀷锛歱layer, activity_player
     targetId: ID!          # 鐩爣ID
-    url: String!           # COS鏂囦欢URL
-    thumbUrl: String       # 缂╃暐鍥綰RL锛堝彲閫夛級
+    path: String!          # COS鏂囦欢璺緞
+    thumbPath: String      # 缂╃暐鍥捐矾寰勶紙鍙�夛級
     fileName: String       # 鏂囦欢鍚�
     fileExt: String        # 鏂囦欢鎵╁睍鍚�
     fileSize: Long         # 鏂囦欢澶у皬锛堝瓧鑺傦級
diff --git a/backend/src/main/resources/graphql/player.graphqls b/backend/src/main/resources/graphql/player.graphqls
index b8e992f..6246211 100644
--- a/backend/src/main/resources/graphql/player.graphqls
+++ b/backend/src/main/resources/graphql/player.graphqls
@@ -1,5 +1,5 @@
 extend type Query {
-  activityPlayerApplications(name: String, activityId: ID, page: Int, size: Int): [ActivityPlayerApplicationResponse!]!
+  activityPlayerApplications(name: String, activityId: ID, state: Int, page: Int, size: Int): [ActivityPlayerApplicationResponse!]!
   activityPlayerDetail(id: ID!): ActivityPlayerDetailResponse
   
   # 鎶ュ悕鐘舵�佹煡璇�
@@ -10,17 +10,32 @@
   currentJudgeRating(activityPlayerId: ID!): CurrentJudgeRatingResponse
   averageScoreForPlayer(activityPlayerId: ID!): Float
   currentJudgeInfo: CurrentJudgeInfoResponse
+  
+  # 姣旇禌鏅嬬骇鐩稿叧鏌ヨ
+  promotionCompetitions(name: String, page: Int, size: Int): [PromotionCompetitionResponse!]!
+  competitionParticipants(competitionId: ID!, page: Int, size: Int): [CompetitionParticipantResponse!]!
+  promotableParticipants(currentStageId: ID!): PromotableParticipantsResponse!
 }
 
 extend type Mutation {
   saveActivityPlayerRating(input: ActivityPlayerRatingInput!): Boolean!
   submitActivityRegistration(input: ActivityRegistrationInput!): ActivityRegistrationResponse!
+  updateActivityRegistration(activityPlayerId: ID!, input: ActivityRegistrationInput!): ActivityRegistrationResponse!
+  
+  # 瀹℃牳鐩稿叧mutations
+  approveActivityPlayer(activityPlayerId: ID!, feedback: String): Boolean!
+  rejectActivityPlayer(activityPlayerId: ID!, feedback: String!): Boolean!
+  updatePlayerFeedback(activityPlayerId: ID!, feedback: String!): Boolean!
+  
+  # 姣旇禌鏅嬬骇鐩稿叧mutations
+  promoteParticipants(input: PromotionInput!): PromotionResult!
 }
 
 type ActivityPlayerApplicationResponse {
   id: ID
   playerName: String!
   activityName: String!
+  projectName: String
   phone: String
   applyTime: String!
   state: Int
@@ -32,18 +47,26 @@
   playerInfo: PlayerInfoResponse!
   regionInfo: RegionInfoResponse
   activityName: String!
+  projectName: String
   description: String
+  feedback: String
+  state: Int
   submissionFiles: [SubmissionMediaResponse!]!
   ratingForm: RatingFormResponse
 }
 
 # 瀛﹀憳淇℃伅鍝嶅簲
 type PlayerInfoResponse {
-  id: ID!
-  name: String!
-  phone: String
-  description: String
-  avatarUrl: String
+    id: ID
+    name: String
+    phone: String
+    description: String
+    avatarUrl: String
+    avatar: MediaResponse
+    gender: Int
+    birthday: String
+    education: String
+    introduction: String
 }
 
 # 鍖哄煙淇℃伅鍝嶅簲
@@ -61,6 +84,7 @@
   fileExt: String
   fileSize: Int
   mediaType: Int
+  thumbUrl: String
 }
 
 # 璇勫垎琛ㄥ崟鍝嶅簲
@@ -177,4 +201,68 @@
   registrationTime: String
   reviewStatus: Int
   reviewComment: String
+}
+
+# 姣旇禌鏅嬬骇鐩稿叧绫诲瀷瀹氫箟
+
+# 姣旇禌鏅嬬骇鍒楄〃鍝嶅簲绫诲瀷
+type PromotionCompetitionResponse {
+  id: ID!
+  competitionName: String!
+  stageName: String!
+  maxParticipants: Int
+  currentCount: Int!
+  status: Int!
+  startTime: String
+  endTime: String
+  sortOrder: Int
+  state: Int
+}
+
+# 姣旇禌鍙傝禌鑰呭搷搴旂被鍨�
+type CompetitionParticipantResponse {
+  id: ID!
+  playerName: String!
+  projectName: String
+  phone: String
+  averageScore: Float
+  ratingCount: Int!
+  applyTime: String!
+  state: Int!
+}
+
+# 鏅嬬骇鎿嶄綔杈撳叆绫诲瀷
+input PromotionInput {
+  competitionId: ID!
+  participantIds: [ID!]!
+  targetStageId: ID
+}
+
+# 鏅嬬骇鎿嶄綔缁撴灉绫诲瀷
+type PromotionResult {
+  success: Boolean!
+  message: String!
+  promotedCount: Int!
+}
+
+# 鍙檵绾у弬璧涜�呭搷搴旂被鍨�
+type PromotableParticipantResponse {
+  id: ID!
+  playerId: ID!
+  playerName: String!
+  projectName: String
+  phone: String
+  averageScore: Float
+  ratingCount: Int!
+  applyTime: String!
+  state: Int!
+}
+
+# 鍙檵绾у弬璧涜�呭垪琛ㄥ搷搴旂被鍨�
+type PromotableParticipantsResponse {
+  participants: [PromotableParticipantResponse!]!
+  selectableCount: Int!
+  totalCount: Int!
+  previousStageName: String!
+  currentStageName: String!
 }
\ No newline at end of file
diff --git a/backend/src/main/resources/graphql/rating.graphqls b/backend/src/main/resources/graphql/rating.graphqls
index c22dfae..d563e9a 100644
--- a/backend/src/main/resources/graphql/rating.graphqls
+++ b/backend/src/main/resources/graphql/rating.graphqls
@@ -27,6 +27,8 @@
     name: String!
     description: String
     totalScore: Int!
+    state: Int!
+    stateName: String!
     items: [RatingItemResponse!]
     createTime: String
     updateTime: String
@@ -38,6 +40,8 @@
     name: String!
     description: String
     totalScore: Int!
+    state: Int!
+    stateName: String!
     items: [RatingItemResponse!]
     createTime: String
     updateTime: String
@@ -57,6 +61,7 @@
     totalElements: ID!
     totalPages: Int!
     number: Int!
+    page: Int!
     size: Int!
     first: Boolean!
     last: Boolean!
diff --git a/backend/src/test/java/com/rongyichuang/AssignActivityPermissionTest.java b/backend/src/test/java/com/rongyichuang/AssignActivityPermissionTest.java
new file mode 100644
index 0000000..f21a80c
--- /dev/null
+++ b/backend/src/test/java/com/rongyichuang/AssignActivityPermissionTest.java
@@ -0,0 +1,96 @@
+package com.rongyichuang;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+import java.util.Map;
+
+@SpringBootTest
+@ActiveProfiles("test")
+public class AssignActivityPermissionTest {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Test
+    public void assignActivityPermissionToJudge68() {
+        System.out.println("=== 涓鸿瘎濮擨D=68鍒嗛厤娲诲姩鏉冮檺 ===");
+        
+        try {
+            Long judgeId = 68L;
+            
+            // 1. 纭璇勫瀛樺湪
+            String checkJudgeSql = "SELECT id, name, user_id FROM t_judge WHERE id = ?";
+            Map<String, Object> judge = jdbcTemplate.queryForMap(checkJudgeSql, judgeId);
+            System.out.println("璇勫淇℃伅: ID=" + judge.get("id") + 
+                             ", 濮撳悕=" + judge.get("name") + 
+                             ", 鐢ㄦ埛ID=" + judge.get("user_id"));
+            
+            // 2. 鏌ョ湅鏈�鏂扮殑鍑犱釜娲诲姩
+            String getActivitiesSql = "SELECT id, name, state FROM t_activity WHERE state = 1 ORDER BY id DESC LIMIT 5";
+            List<Map<String, Object>> activities = jdbcTemplate.queryForList(getActivitiesSql);
+            
+            System.out.println("鏈�鏂扮殑娲诲姩鍒楄〃:");
+            for (Map<String, Object> activity : activities) {
+                System.out.println("  娲诲姩ID: " + activity.get("id") + 
+                                 ", 娲诲姩鍚嶇О: " + activity.get("name") + 
+                                 ", 鐘舵��: " + activity.get("state"));
+            }
+            
+            // 3. 涓鸿瘎濮斿垎閰嶆渶鏂版椿鍔ㄧ殑鏉冮檺
+            if (!activities.isEmpty()) {
+                Map<String, Object> latestActivity = activities.get(0);
+                Long activityId = ((Number) latestActivity.get("id")).longValue();
+                String activityName = (String) latestActivity.get("name");
+                
+                // 妫�鏌ユ槸鍚﹀凡缁忔湁鏉冮檺
+                String checkPermissionSql = "SELECT COUNT(*) as count FROM t_activity_judge WHERE judge_id = ? AND activity_id = ?";
+                Map<String, Object> permissionExists = jdbcTemplate.queryForMap(checkPermissionSql, judgeId, activityId);
+                
+                if (((Number) permissionExists.get("count")).intValue() == 0) {
+                    // 鍩轰簬鐜版湁璁板綍澶嶅埗stage_id锛屼娇鐢ㄦ椿鍔↖D浣滀负stage_id锛堢畝鍖栧鐞嗭級
+                    String insertPermissionSql = "INSERT INTO t_activity_judge (judge_id, activity_id, stage_id, state, version) VALUES (?, ?, ?, 1, 0)";
+                    int result = jdbcTemplate.update(insertPermissionSql, judgeId, activityId, activityId);
+                    
+                    if (result > 0) {
+                        System.out.println("鉁� 鎴愬姛涓鸿瘎濮斿垎閰嶆椿鍔ㄦ潈闄�: " + activityName + " (ID=" + activityId + ")");
+                    } else {
+                        System.out.println("鉂� 鍒嗛厤娲诲姩鏉冮檺澶辫触");
+                    }
+                } else {
+                    System.out.println("璇勫宸茬粡鏈夎娲诲姩鐨勬潈闄�");
+                }
+                
+                // 4. 楠岃瘉鏉冮檺鍒嗛厤缁撴灉
+                String verifyPermissionSql = "SELECT aj.activity_id, a.name as activity_name, aj.state " +
+                                           "FROM t_activity_judge aj " +
+                                           "JOIN t_activity a ON aj.activity_id = a.id " +
+                                           "WHERE aj.judge_id = ?";
+                List<Map<String, Object>> permissions = jdbcTemplate.queryForList(verifyPermissionSql, judgeId);
+                
+                System.out.println("璇勫ID=" + judgeId + " 鐨勬墍鏈夋椿鍔ㄦ潈闄�:");
+                for (Map<String, Object> permission : permissions) {
+                    System.out.println("  娲诲姩ID: " + permission.get("activity_id") + 
+                                     ", 娲诲姩鍚嶇О: " + permission.get("activity_name") + 
+                                     ", 鏉冮檺鐘舵��: " + permission.get("state"));
+                }
+                
+                if (permissions.isEmpty()) {
+                    System.out.println("鉂� 璇ヨ瘎濮斾粛鐒舵病鏈変换浣曟椿鍔ㄦ潈闄愶紒");
+                } else {
+                    System.out.println("鉁� 鏉冮檺鍒嗛厤鎴愬姛锛岃瘎濮旂幇鍦ㄦ湁 " + permissions.size() + " 涓椿鍔ㄧ殑璇勫鏉冮檺");
+                }
+            } else {
+                System.out.println("鉂� 娌℃湁鎵惧埌鍙敤鐨勬椿鍔�");
+            }
+            
+        } catch (Exception e) {
+            System.out.println("鍒嗛厤鏉冮檺鏃跺彂鐢熷紓甯�: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/backend/src/test/java/com/rongyichuang/CheckActivityJudgeTableTest.java b/backend/src/test/java/com/rongyichuang/CheckActivityJudgeTableTest.java
new file mode 100644
index 0000000..3861d97
--- /dev/null
+++ b/backend/src/test/java/com/rongyichuang/CheckActivityJudgeTableTest.java
@@ -0,0 +1,50 @@
+package com.rongyichuang;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+import java.util.Map;
+
+@SpringBootTest
+@ActiveProfiles("test")
+public class CheckActivityJudgeTableTest {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Test
+    public void checkActivityJudgeTableStructure() {
+        System.out.println("=== 妫�鏌_activity_judge琛ㄧ粨鏋� ===");
+        
+        try {
+            // 鏌ョ湅琛ㄧ粨鏋�
+            String describeTableSql = "DESCRIBE t_activity_judge";
+            List<Map<String, Object>> columns = jdbcTemplate.queryForList(describeTableSql);
+            
+            System.out.println("t_activity_judge琛ㄥ瓧娈�:");
+            for (Map<String, Object> column : columns) {
+                System.out.println("  瀛楁鍚�: " + column.get("Field") + 
+                                 ", 绫诲瀷: " + column.get("Type") + 
+                                 ", 鏄惁涓虹┖: " + column.get("Null") + 
+                                 ", 榛樿鍊�: " + column.get("Default"));
+            }
+            
+            // 鏌ョ湅鐜版湁鐨勬椿鍔�-璇勫鍏宠仈璁板綍
+            String selectRecordsSql = "SELECT * FROM t_activity_judge LIMIT 3";
+            List<Map<String, Object>> records = jdbcTemplate.queryForList(selectRecordsSql);
+            
+            System.out.println("\n鐜版湁娲诲姩-璇勫鍏宠仈璁板綍绀轰緥:");
+            for (Map<String, Object> record : records) {
+                System.out.println("  " + record);
+            }
+            
+        } catch (Exception e) {
+            System.out.println("妫�鏌ヨ〃缁撴瀯鏃跺彂鐢熷紓甯�: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/backend/src/test/java/com/rongyichuang/CheckJudgeTableTest.java b/backend/src/test/java/com/rongyichuang/CheckJudgeTableTest.java
new file mode 100644
index 0000000..125552d
--- /dev/null
+++ b/backend/src/test/java/com/rongyichuang/CheckJudgeTableTest.java
@@ -0,0 +1,50 @@
+package com.rongyichuang;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+import java.util.Map;
+
+@SpringBootTest
+@ActiveProfiles("test")
+public class CheckJudgeTableTest {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Test
+    public void checkJudgeTableStructure() {
+        System.out.println("=== 妫�鏌_judge琛ㄧ粨鏋� ===");
+        
+        try {
+            // 鏌ョ湅琛ㄧ粨鏋�
+            String describeTableSql = "DESCRIBE t_judge";
+            List<Map<String, Object>> columns = jdbcTemplate.queryForList(describeTableSql);
+            
+            System.out.println("t_judge琛ㄥ瓧娈�:");
+            for (Map<String, Object> column : columns) {
+                System.out.println("  瀛楁鍚�: " + column.get("Field") + 
+                                 ", 绫诲瀷: " + column.get("Type") + 
+                                 ", 鏄惁涓虹┖: " + column.get("Null") + 
+                                 ", 榛樿鍊�: " + column.get("Default"));
+            }
+            
+            // 鏌ョ湅鐜版湁鐨勮瘎濮旇褰�
+            String selectJudgesSql = "SELECT * FROM t_judge LIMIT 3";
+            List<Map<String, Object>> judges = jdbcTemplate.queryForList(selectJudgesSql);
+            
+            System.out.println("\n鐜版湁璇勫璁板綍绀轰緥:");
+            for (Map<String, Object> judge : judges) {
+                System.out.println("  " + judge);
+            }
+            
+        } catch (Exception e) {
+            System.out.println("妫�鏌ヨ〃缁撴瀯鏃跺彂鐢熷紓甯�: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/backend/src/test/java/com/rongyichuang/CheckMediaRecordsTest.java b/backend/src/test/java/com/rongyichuang/CheckMediaRecordsTest.java
new file mode 100644
index 0000000..8f6fc60
--- /dev/null
+++ b/backend/src/test/java/com/rongyichuang/CheckMediaRecordsTest.java
@@ -0,0 +1,121 @@
+package com.rongyichuang;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import java.util.List;
+import java.util.Map;
+
+@SpringBootTest
+public class CheckMediaRecordsTest {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Test
+    public void checkMediaRecords() {
+        System.out.println("=== 妫�鏌ユ暟鎹簱琛ㄧ粨鏋� ===");
+        
+        try {
+            // 棣栧厛鏌ョ湅鎵�鏈夎〃
+            String showTables = "SHOW TABLES";
+            List<Map<String, Object>> tables = jdbcTemplate.queryForList(showTables);
+            
+            System.out.println("鏁版嵁搴撲腑鐨勮〃:");
+            for (Map<String, Object> table : tables) {
+                System.out.println("- " + table.values().iterator().next());
+            }
+            
+            // 妫�鏌ユ槸鍚︽湁media鐩稿叧鐨勮〃
+            System.out.println("\n=== 妫�鏌edia鐩稿叧琛� ===");
+            for (Map<String, Object> table : tables) {
+                String tableName = table.values().iterator().next().toString();
+                if (tableName.toLowerCase().contains("media")) {
+                    System.out.println("鎵惧埌media鐩稿叧琛�: " + tableName);
+                    
+                    try {
+                        // 鏌ョ湅琛ㄧ粨鏋�
+                        String descTable = "DESC " + tableName;
+                        List<Map<String, Object>> columns = jdbcTemplate.queryForList(descTable);
+                        System.out.println("琛ㄧ粨鏋�:");
+                        for (Map<String, Object> column : columns) {
+                            System.out.println("  " + column.get("Field") + " - " + column.get("Type"));
+                        }
+                        
+                        // 鏌ョ湅璁板綍鏁伴噺
+                        String countSql = "SELECT COUNT(*) as count FROM " + tableName;
+                        Map<String, Object> countResult = jdbcTemplate.queryForMap(countSql);
+                        System.out.println("璁板綍鏁伴噺: " + countResult.get("count"));
+                        
+                        // 濡傛灉鏈夎褰曪紝鏌ョ湅鏈�杩戝嚑鏉�
+                        Long count = (Long) countResult.get("count");
+                        if (count > 0) {
+                            String sampleSql = "SELECT * FROM " + tableName + " ORDER BY id DESC LIMIT 3";
+                            List<Map<String, Object>> samples = jdbcTemplate.queryForList(sampleSql);
+                            System.out.println("鏈�杩�3鏉¤褰�:");
+                            for (Map<String, Object> sample : samples) {
+                                System.out.println("  " + sample);
+                            }
+                            
+                            // 濡傛灉鏄痶_media琛紝涓撻棬鏌ヨtargetType=5鐨勮褰�
+                            if ("t_media".equals(tableName)) {
+                                try {
+                                    List<Map<String, Object>> targetType5Records = jdbcTemplate.queryForList(
+                                        "SELECT * FROM t_media WHERE target_type = 5 ORDER BY create_time DESC"
+                                    );
+                                    System.out.println("targetType=5鐨勮褰曟暟閲�: " + targetType5Records.size());
+                                    if (!targetType5Records.isEmpty()) {
+                                        System.out.println("targetType=5鐨勮褰�:");
+                                        for (Map<String, Object> record : targetType5Records) {
+                                            System.out.println("  " + record);
+                                        }
+                                    } else {
+                                        System.out.println("娌℃湁鎵惧埌targetType=5鐨勮褰�");
+                                    }
+                                } catch (Exception e) {
+                                    System.out.println("鏌ヨtargetType=5璁板綍澶辫触: " + e.getMessage());
+                                }
+                            }
+                        }
+                    } catch (Exception e) {
+                        System.out.println("鏌ヨ琛� " + tableName + " 鏃跺嚭閿�: " + e.getMessage());
+                    }
+                    System.out.println();
+                }
+            }
+            
+            // 鏌ョ湅activity_player鐩稿叧鐨勮〃
+            System.out.println("=== 妫�鏌ctivity_player鐩稿叧琛� ===");
+            for (Map<String, Object> table : tables) {
+                String tableName = table.values().iterator().next().toString();
+                if (tableName.toLowerCase().contains("activity") && tableName.toLowerCase().contains("player")) {
+                    System.out.println("鎵惧埌activity_player鐩稿叧琛�: " + tableName);
+                    
+                    try {
+                        // 鏌ョ湅璁板綍鏁伴噺
+                        String countSql = "SELECT COUNT(*) as count FROM " + tableName;
+                        Map<String, Object> countResult = jdbcTemplate.queryForMap(countSql);
+                        System.out.println("璁板綍鏁伴噺: " + countResult.get("count"));
+                        
+                        // 鏌ョ湅鏈�杩戝嚑鏉¤褰�
+                        String sampleSql = "SELECT * FROM " + tableName + " ORDER BY id DESC LIMIT 3";
+                        List<Map<String, Object>> samples = jdbcTemplate.queryForList(sampleSql);
+                        System.out.println("鏈�杩�3鏉¤褰�:");
+                        for (Map<String, Object> sample : samples) {
+                            System.out.println("  " + sample);
+                        }
+                    } catch (Exception e) {
+                        System.out.println("鏌ヨ琛� " + tableName + " 鏃跺嚭閿�: " + e.getMessage());
+                    }
+                    System.out.println();
+                }
+            }
+            
+        } catch (Exception e) {
+            System.out.println("鏁版嵁搴撴煡璇㈠嚭閿�: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/backend/src/test/java/com/rongyichuang/CheckUserPermissionTest.java b/backend/src/test/java/com/rongyichuang/CheckUserPermissionTest.java
new file mode 100644
index 0000000..deb5eaf
--- /dev/null
+++ b/backend/src/test/java/com/rongyichuang/CheckUserPermissionTest.java
@@ -0,0 +1,97 @@
+package com.rongyichuang;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+import java.util.Map;
+
+@SpringBootTest
+@ActiveProfiles("test")
+public class CheckUserPermissionTest {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Test
+    public void checkUserJudgePermission() {
+        System.out.println("=== 妫�鏌ョ敤鎴稩D=2鐨勮瘎濮旀潈闄� ===");
+        
+        try {
+            // 鍏堟煡鐪嬬敤鎴疯〃缁撴瀯
+            String describeUserSql = "DESCRIBE t_user";
+            List<Map<String, Object>> userTableStructure = jdbcTemplate.queryForList(describeUserSql);
+            System.out.println("鐢ㄦ埛琛ㄧ粨鏋�:");
+            for (Map<String, Object> column : userTableStructure) {
+                System.out.println("瀛楁: " + column.get("Field") + ", 绫诲瀷: " + column.get("Type"));
+            }
+            
+            // 1. 妫�鏌ョ敤鎴稩D=2鏄惁鍏宠仈浜嗚瘎濮旇褰�
+            String userJudgeSql = "SELECT u.id as user_id, u.phone, j.id as judge_id, j.name as judge_name " +
+                                 "FROM t_user u LEFT JOIN t_judge j ON u.id = j.user_id WHERE u.id = 2";
+            List<Map<String, Object>> userJudgeData = jdbcTemplate.queryForList(userJudgeSql);
+            
+            System.out.println("\n鐢ㄦ埛璇勫鍏宠仈鏁版嵁:");
+            for (Map<String, Object> row : userJudgeData) {
+                System.out.println("鐢ㄦ埛ID: " + row.get("user_id") + 
+                                 ", 鎵嬫満鍙�: " + row.get("phone") + 
+                                 ", 璇勫ID: " + row.get("judge_id") + 
+                                 ", 璇勫濮撳悕: " + row.get("judge_name"));
+            }
+            
+            // 2. 濡傛灉鏈夎瘎濮旇褰曪紝妫�鏌ヨ瘎濮旂殑娲诲姩鏉冮檺
+            if (!userJudgeData.isEmpty() && userJudgeData.get(0).get("judge_id") != null) {
+                Long judgeId = ((Number) userJudgeData.get(0).get("judge_id")).longValue();
+                
+                String activityJudgeSql = "SELECT aj.activity_id, aj.judge_id, aj.state, a.name as activity_name " +
+                                         "FROM t_activity_judge aj " +
+                                         "LEFT JOIN t_activity a ON aj.activity_id = a.id " +
+                                         "WHERE aj.judge_id = ?";
+                List<Map<String, Object>> activityJudgeData = jdbcTemplate.queryForList(activityJudgeSql, judgeId);
+                
+                System.out.println("\n璇勫娲诲姩鏉冮檺鏁版嵁:");
+                for (Map<String, Object> row : activityJudgeData) {
+                    System.out.println("娲诲姩ID: " + row.get("activity_id") + 
+                                     ", 娲诲姩鍚嶇О: " + row.get("activity_name") + 
+                                     ", 璇勫ID: " + row.get("judge_id") + 
+                                     ", 鐘舵��: " + row.get("state"));
+                }
+                
+                if (activityJudgeData.isEmpty()) {
+                    System.out.println("鉂� 璇勫ID=" + judgeId + " 娌℃湁浠讳綍娲诲姩鐨勮瘎瀹℃潈闄�");
+                }
+            } else {
+                System.out.println("鉂� 鐢ㄦ埛ID=2 娌℃湁鍏宠仈璇勫璁板綍");
+            }
+            
+            // 3. 妫�鏌ユ墍鏈夋椿鍔ㄧ殑淇℃伅
+            String allActivitiesSql = "SELECT id, name, state FROM t_activity ORDER BY id";
+            List<Map<String, Object>> allActivities = jdbcTemplate.queryForList(allActivitiesSql);
+            
+            System.out.println("\n鎵�鏈夋椿鍔ㄤ俊鎭�:");
+            for (Map<String, Object> row : allActivities) {
+                System.out.println("娲诲姩ID: " + row.get("id") + 
+                                 ", 娲诲姩鍚嶇О: " + row.get("name") + 
+                                 ", 鐘舵��: " + row.get("state"));
+            }
+            
+            // 4. 妫�鏌ユ墍鏈夎瘎濮斾俊鎭�
+            String allJudgesSql = "SELECT id, name, user_id FROM t_judge ORDER BY id";
+            List<Map<String, Object>> allJudges = jdbcTemplate.queryForList(allJudgesSql);
+            
+            System.out.println("\n鎵�鏈夎瘎濮斾俊鎭�:");
+            for (Map<String, Object> row : allJudges) {
+                System.out.println("璇勫ID: " + row.get("id") + 
+                                 ", 璇勫濮撳悕: " + row.get("name") + 
+                                 ", 鐢ㄦ埛ID: " + row.get("user_id"));
+            }
+            
+        } catch (Exception e) {
+            System.out.println("妫�鏌ユ潈闄愭椂鍙戠敓寮傚父: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/backend/src/test/java/com/rongyichuang/CreateJudgeForUser2Test.java b/backend/src/test/java/com/rongyichuang/CreateJudgeForUser2Test.java
new file mode 100644
index 0000000..272a3ec
--- /dev/null
+++ b/backend/src/test/java/com/rongyichuang/CreateJudgeForUser2Test.java
@@ -0,0 +1,107 @@
+package com.rongyichuang;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+import java.util.Map;
+
+@SpringBootTest
+@ActiveProfiles("test")
+public class CreateJudgeForUser2Test {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Test
+    public void createJudgeForUser2() {
+        System.out.println("=== 涓虹敤鎴稩D=2鍒涘缓璇勫璁板綍 ===");
+        
+        try {
+            // 1. 棣栧厛妫�鏌ョ敤鎴稩D=2鐨勫熀鏈俊鎭�
+            String getUserInfoSql = "SELECT id, phone, name FROM t_user WHERE id = 2";
+            Map<String, Object> userInfo = jdbcTemplate.queryForMap(getUserInfoSql);
+            System.out.println("鐢ㄦ埛淇℃伅: ID=" + userInfo.get("id") + 
+                             ", 鎵嬫満鍙�=" + userInfo.get("phone") + 
+                             ", 濮撳悕=" + userInfo.get("name"));
+            
+            // 2. 妫�鏌ユ槸鍚﹀凡缁忔湁璇勫璁板綍
+            String checkJudgeSql = "SELECT COUNT(*) as count FROM t_judge WHERE user_id = 2";
+            Map<String, Object> judgeExists = jdbcTemplate.queryForMap(checkJudgeSql);
+            
+            if (((Number) judgeExists.get("count")).intValue() > 0) {
+                System.out.println("鐢ㄦ埛ID=2宸茬粡鏈夎瘎濮旇褰曪紝鏃犻渶閲嶅鍒涘缓");
+                return;
+            }
+            
+            // 3. 鍒涘缓璇勫璁板綍
+            String userName = (String) userInfo.get("name");
+            String userPhone = (String) userInfo.get("phone");
+            if (userName == null || userName.trim().isEmpty()) {
+                userName = "璇勫_" + userPhone; // 濡傛灉娌℃湁濮撳悕锛屼娇鐢ㄦ墜鏈哄彿
+            }
+            
+            // 鍏堟煡鐪嬬幇鏈夎瘎濮旇褰曠殑瀛楁锛屽鍒朵竴涓被浼肩殑璁板綍
+            String copyExistingJudgeSql = "INSERT INTO t_judge (name, user_id, phone, gender, state, description, version, stage_id) " +
+                                        "SELECT ?, ?, ?, gender, state, '绯荤粺鑷姩鍒涘缓鐨勮瘎濮�', 0, stage_id FROM t_judge WHERE id = 65 LIMIT 1";
+            int result = jdbcTemplate.update(copyExistingJudgeSql, userName, 2, userPhone);
+            
+            if (result > 0) {
+                System.out.println("鉁� 鎴愬姛涓虹敤鎴稩D=2鍒涘缓璇勫璁板綍");
+                
+                // 4. 鑾峰彇鏂板垱寤虹殑璇勫ID
+                String getNewJudgeIdSql = "SELECT id, name FROM t_judge WHERE user_id = 2";
+                Map<String, Object> newJudge = jdbcTemplate.queryForMap(getNewJudgeIdSql);
+                Long judgeId = ((Number) newJudge.get("id")).longValue();
+                String judgeName = (String) newJudge.get("name");
+                
+                System.out.println("鏂拌瘎濮斾俊鎭�: ID=" + judgeId + ", 濮撳悕=" + judgeName);
+                
+                // 5. 鏌ョ湅褰撳墠鏈夊摢浜涙椿鍔ㄥ彲浠ュ垎閰嶆潈闄�
+                String getActivitiesSql = "SELECT id, name, state FROM t_activity WHERE state = 1 ORDER BY id DESC LIMIT 5";
+                List<Map<String, Object>> activities = jdbcTemplate.queryForList(getActivitiesSql);
+                
+                System.out.println("褰撳墠鍙敤鐨勬椿鍔紙鏈�鏂�5涓級:");
+                for (Map<String, Object> activity : activities) {
+                    System.out.println("  娲诲姩ID: " + activity.get("id") + 
+                                     ", 娲诲姩鍚嶇О: " + activity.get("name") + 
+                                     ", 鐘舵��: " + activity.get("state"));
+                }
+                
+                // 6. 涓鸿瘎濮斿垎閰嶆渶鏂版椿鍔ㄧ殑鏉冮檺锛堝亣璁惧垎閰嶆渶鏂扮殑娲诲姩锛�
+                if (!activities.isEmpty()) {
+                    Map<String, Object> latestActivity = activities.get(0);
+                    Long activityId = ((Number) latestActivity.get("id")).longValue();
+                    String activityName = (String) latestActivity.get("name");
+                    
+                    // 妫�鏌ユ槸鍚﹀凡缁忔湁鏉冮檺
+                    String checkPermissionSql = "SELECT COUNT(*) as count FROM t_activity_judge WHERE judge_id = ? AND activity_id = ?";
+                    Map<String, Object> permissionExists = jdbcTemplate.queryForMap(checkPermissionSql, judgeId, activityId);
+                    
+                    if (((Number) permissionExists.get("count")).intValue() == 0) {
+                        String insertPermissionSql = "INSERT INTO t_activity_judge (judge_id, activity_id, state, create_time, update_time) VALUES (?, ?, 1, NOW(), NOW())";
+                        int permissionResult = jdbcTemplate.update(insertPermissionSql, judgeId, activityId);
+                        
+                        if (permissionResult > 0) {
+                            System.out.println("鉁� 鎴愬姛涓鸿瘎濮斿垎閰嶆椿鍔ㄦ潈闄�: " + activityName + " (ID=" + activityId + ")");
+                        } else {
+                            System.out.println("鉂� 鍒嗛厤娲诲姩鏉冮檺澶辫触");
+                        }
+                    } else {
+                        System.out.println("璇勫宸茬粡鏈夎娲诲姩鐨勬潈闄�");
+                    }
+                }
+                
+            } else {
+                System.out.println("鉂� 鍒涘缓璇勫璁板綍澶辫触");
+            }
+            
+        } catch (Exception e) {
+            System.out.println("鍒涘缓璇勫璁板綍鏃跺彂鐢熷紓甯�: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/backend/src/test/java/com/rongyichuang/DatabaseSchemaTest.java b/backend/src/test/java/com/rongyichuang/DatabaseSchemaTest.java
index 74d8b40..39b5ef9 100644
--- a/backend/src/test/java/com/rongyichuang/DatabaseSchemaTest.java
+++ b/backend/src/test/java/com/rongyichuang/DatabaseSchemaTest.java
@@ -58,4 +58,20 @@
             System.out.println(table);
         }
     }
+
+    @Test
+    public void testForeignKeyConstraints() {
+        try {
+            String sql = "SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME " +
+                        "FROM information_schema.KEY_COLUMN_USAGE " +
+                        "WHERE TABLE_SCHEMA = 'ryc' AND REFERENCED_TABLE_NAME IS NOT NULL";
+            List<Map<String, Object>> result = jdbcTemplate.queryForList(sql);
+            System.out.println("=== 澶栭敭绾︽潫 ===");
+            for (Map<String, Object> row : result) {
+                System.out.println(row);
+            }
+        } catch (Exception e) {
+            System.out.println("鏌ヨ澶栭敭绾︽潫澶辫触: " + e.getMessage());
+        }
+    }
 }
\ No newline at end of file
diff --git a/backend/src/test/java/com/rongyichuang/SimpleUserCheckTest.java b/backend/src/test/java/com/rongyichuang/SimpleUserCheckTest.java
new file mode 100644
index 0000000..a72da69
--- /dev/null
+++ b/backend/src/test/java/com/rongyichuang/SimpleUserCheckTest.java
@@ -0,0 +1,79 @@
+package com.rongyichuang;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+import java.util.Map;
+
+@SpringBootTest
+@ActiveProfiles("test")
+public class SimpleUserCheckTest {
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Test
+    public void checkUser2Permission() {
+        System.out.println("=== 妫�鏌ョ敤鎴稩D=2鐨勮瘎濮旀潈闄愰棶棰� ===");
+        
+        try {
+            // 1. 妫�鏌ョ敤鎴稩D=2鏄惁瀛樺湪
+            String userExistsSql = "SELECT COUNT(*) as count FROM t_user WHERE id = 2";
+            Map<String, Object> userExists = jdbcTemplate.queryForMap(userExistsSql);
+            System.out.println("鐢ㄦ埛ID=2鏄惁瀛樺湪: " + userExists.get("count"));
+            
+            // 2. 妫�鏌ョ敤鎴稩D=2鏄惁鍏宠仈浜嗚瘎濮�
+            String judgeExistsSql = "SELECT COUNT(*) as count FROM t_judge WHERE user_id = 2";
+            Map<String, Object> judgeExists = jdbcTemplate.queryForMap(judgeExistsSql);
+            System.out.println("鐢ㄦ埛ID=2鍏宠仈鐨勮瘎濮旀暟閲�: " + judgeExists.get("count"));
+            
+            // 3. 濡傛灉鏈夎瘎濮旓紝鑾峰彇璇勫ID
+            if (((Number) judgeExists.get("count")).intValue() > 0) {
+                String getJudgeIdSql = "SELECT id, name FROM t_judge WHERE user_id = 2";
+                List<Map<String, Object>> judges = jdbcTemplate.queryForList(getJudgeIdSql);
+                for (Map<String, Object> judge : judges) {
+                    Long judgeId = ((Number) judge.get("id")).longValue();
+                    String judgeName = (String) judge.get("name");
+                    System.out.println("璇勫ID: " + judgeId + ", 璇勫濮撳悕: " + judgeName);
+                    
+                    // 4. 妫�鏌ヨ璇勫鐨勬椿鍔ㄦ潈闄�
+                    String activityPermissionSql = "SELECT COUNT(*) as count FROM t_activity_judge WHERE judge_id = ?";
+                    Map<String, Object> activityPermission = jdbcTemplate.queryForMap(activityPermissionSql, judgeId);
+                    System.out.println("璇勫ID=" + judgeId + " 鐨勬椿鍔ㄦ潈闄愭暟閲�: " + activityPermission.get("count"));
+                    
+                    // 5. 鍒楀嚭鍏蜂綋鐨勬椿鍔ㄦ潈闄�
+                    if (((Number) activityPermission.get("count")).intValue() > 0) {
+                        String activityDetailsSql = "SELECT activity_id, state FROM t_activity_judge WHERE judge_id = ?";
+                        List<Map<String, Object>> activities = jdbcTemplate.queryForList(activityDetailsSql, judgeId);
+                        System.out.println("鍏蜂綋娲诲姩鏉冮檺:");
+                        for (Map<String, Object> activity : activities) {
+                            System.out.println("  娲诲姩ID: " + activity.get("activity_id") + ", 鐘舵��: " + activity.get("state"));
+                        }
+                    } else {
+                        System.out.println("鉂� 璇ヨ瘎濮旀病鏈変换浣曟椿鍔ㄧ殑璇勫鏉冮檺锛�");
+                    }
+                }
+            } else {
+                System.out.println("鉂� 鐢ㄦ埛ID=2 娌℃湁鍏宠仈浠讳綍璇勫璁板綍锛�");
+                
+                // 鏌ョ湅鎵�鏈夋湁鐢ㄦ埛ID鐨勮瘎濮�
+                String allJudgesWithUserSql = "SELECT id, name, user_id FROM t_judge WHERE user_id IS NOT NULL";
+                List<Map<String, Object>> allJudges = jdbcTemplate.queryForList(allJudgesWithUserSql);
+                System.out.println("鎵�鏈夋湁鐢ㄦ埛ID鐨勮瘎濮�:");
+                for (Map<String, Object> judge : allJudges) {
+                    System.out.println("  璇勫ID: " + judge.get("id") + 
+                                     ", 濮撳悕: " + judge.get("name") + 
+                                     ", 鐢ㄦ埛ID: " + judge.get("user_id"));
+                }
+            }
+            
+        } catch (Exception e) {
+            System.out.println("妫�鏌ユ潈闄愭椂鍙戠敓寮傚父: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/backend/test_media_api_integration.js b/backend/test_media_api_integration.js
deleted file mode 100644
index d553cb2..0000000
--- a/backend/test_media_api_integration.js
+++ /dev/null
@@ -1,133 +0,0 @@
-// 浣跨敤鍐呯疆鐨刦etch API (Node.js 18+)
-
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql';
-
-// 鐢熸垚鍞竴鐨勬祴璇曟暟鎹�
-const timestamp = Date.now();
-const uniquePhone = `1380000${timestamp.toString().slice(-4)}`;
-const uniqueAvatarMediaId = `avatar_${timestamp}`;
-const uniqueAttachmentMediaIds = [`attachment_${timestamp}_1`, `attachment_${timestamp}_2`];
-
-async function testMediaApiIntegration() {
-    console.log('寮�濮嬫祴璇曞獟浣揂PI闆嗘垚...');
-    
-    try {
-        // 1. 鎻愪氦娲诲姩鎶ュ悕锛堝寘鍚玜vatarMediaId鍜宎ttachmentMediaIds锛�
-        console.log('1. 鎻愪氦娲诲姩鎶ュ悕...');
-        const registrationMutation = `
-            mutation {
-                submitActivityRegistration(input: {
-                    activityId: 1,
-                    playerInfo: {
-                        name: "娴嬭瘯鐢ㄦ埛${timestamp}",
-                        phone: "${uniquePhone}",
-                        gender: 1,
-                        birthDate: "1990-01-01",
-                        avatarMediaId: "${uniqueAvatarMediaId}"
-                    },
-                    regionId: 1,
-                    projectName: "娴嬭瘯椤圭洰",
-                    description: "娴嬭瘯鎻忚堪",
-                    attachmentMediaIds: ${JSON.stringify(uniqueAttachmentMediaIds)}
-                }) {
-                    success
-                    message
-                    registrationId
-                    playerId
-                    userId
-                }
-            }
-        `;
-        
-        const registrationResponse = await fetch(GRAPHQL_ENDPOINT, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                query: registrationMutation
-            })
-        });
-        
-        const registrationResult = await registrationResponse.json();
-        console.log('鎶ュ悕缁撴灉:', JSON.stringify(registrationResult, null, 2));
-        
-        if (registrationResult.errors) {
-            console.error('鎶ュ悕澶辫触:', registrationResult.errors);
-            return;
-        }
-        
-        const result = registrationResult.data.submitActivityRegistration;
-        if (!result.success) {
-            console.error('鎶ュ悕澶辫触:', result.message);
-            return;
-        }
-        
-        console.log(`鎶ュ悕鎴愬姛! 鎶ュ悕ID: ${result.registrationId}, 閫夋墜ID: ${result.playerId}`);
-        
-        // 2. 鏌ヨ閫夋墜鍏宠仈鐨勫獟浣撹褰曪紙澶村儚锛�
-        console.log('\\n2. 鏌ヨ閫夋墜澶村儚濯掍綋璁板綍...');
-        const playerMediaQuery = `
-            query {
-                medias(targetType: 1, targetId: ${result.playerId}) {
-                    id
-                    name
-                    path
-                    fileSize
-                    mediaType
-                    targetType
-                    targetId
-                }
-            }
-        `;
-        
-        const playerMediaResponse = await fetch(GRAPHQL_ENDPOINT, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                query: playerMediaQuery
-            })
-        });
-        
-        const playerMediaResult = await playerMediaResponse.json();
-        console.log('閫夋墜濯掍綋璁板綍:', JSON.stringify(playerMediaResult, null, 2));
-        
-        // 3. 鏌ヨ娲诲姩鎶ュ悕鍏宠仈鐨勫獟浣撹褰曪紙闄勪欢锛�
-        console.log('\\n3. 鏌ヨ娲诲姩鎶ュ悕闄勪欢濯掍綋璁板綍...');
-        const attachmentMediaQuery = `
-            query {
-                medias(targetType: 2, targetId: ${result.registrationId}) {
-                    id
-                    name
-                    path
-                    fileSize
-                    mediaType
-                    targetType
-                    targetId
-                }
-            }
-        `;
-        
-        const attachmentMediaResponse = await fetch(GRAPHQL_ENDPOINT, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                query: attachmentMediaQuery
-            })
-        });
-        
-        const attachmentMediaResult = await attachmentMediaResponse.json();
-        console.log('闄勪欢濯掍綋璁板綍:', JSON.stringify(attachmentMediaResult, null, 2));
-        
-        console.log('\\n娴嬭瘯瀹屾垚锛佽妫�鏌ュ悗绔棩蹇楃‘璁ゅ獟浣撹褰曚繚瀛樻儏鍐点��');
-        
-    } catch (error) {
-        console.error('娴嬭瘯杩囩▼涓彂鐢熼敊璇�:', error);
-    }
-}
-
-testMediaApiIntegration();
\ No newline at end of file
diff --git a/backend/test_wechat_login.py b/backend/test_wechat_login.py
deleted file mode 100644
index e029a1d..0000000
--- a/backend/test_wechat_login.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-娴嬭瘯寰俊鐧诲綍GraphQL鎺ュ彛
-"""
-
-import requests
-import json
-
-def test_wechat_login():
-    """娴嬭瘯寰俊鐧诲綍鎺ュ彛"""
-    
-    # GraphQL绔偣
-    url = "http://localhost:8080/api/graphql"
-    
-    # 娴嬭瘯鏌ヨ
-    query = """
-    mutation {
-        wxLogin(input: {
-            code: "test_code_123"
-            loginIp: "127.0.0.1"
-            deviceInfo: "Test Device"
-            phoneAuthorized: false
-        }) {
-            token
-            userInfo {
-                userId
-                name
-                userType
-            }
-            isNewUser
-            loginRecordId
-        }
-    }
-    """
-    
-    # 璇锋眰鏁版嵁
-    data = {
-        "query": query
-    }
-    
-    # 鍙戦�佽姹�
-    headers = {
-        "Content-Type": "application/json"
-    }
-    
-    try:
-        print("=== 娴嬭瘯寰俊鐧诲綍鎺ュ彛 ===")
-        print(f"璇锋眰URL: {url}")
-        print(f"璇锋眰鏁版嵁: {json.dumps(data, indent=2, ensure_ascii=False)}")
-        
-        response = requests.post(url, json=data, headers=headers)
-        
-        print(f"鍝嶅簲鐘舵�佺爜: {response.status_code}")
-        print(f"鍝嶅簲鍐呭: {json.dumps(response.json(), indent=2, ensure_ascii=False)}")
-        
-        if response.status_code == 200:
-            result = response.json()
-            if "errors" in result:
-                print("鉂� GraphQL閿欒:")
-                for error in result["errors"]:
-                    print(f"  - {error.get('message', '鏈煡閿欒')}")
-                    if "extensions" in error:
-                        print(f"    鍒嗙被: {error['extensions'].get('classification', '鏈煡')}")
-            else:
-                print("鉁� 璇锋眰鎴愬姛")
-        else:
-            print(f"鉂� HTTP閿欒: {response.status_code}")
-            
-    except Exception as e:
-        print(f"鉂� 璇锋眰寮傚父: {e}")
-
-if __name__ == "__main__":
-    test_wechat_login()
\ No newline at end of file
diff --git a/check_activity_dates.js b/check_activity_dates.js
deleted file mode 100644
index 0e1417b..0000000
--- a/check_activity_dates.js
+++ /dev/null
@@ -1,94 +0,0 @@
-const mysql = require('mysql2/promise');
-
-async function checkActivityDates() {
-    let connection;
-    
-    try {
-        // 鍒涘缓鏁版嵁搴撹繛鎺�
-        connection = await mysql.createConnection({
-            host: '139.155.104.10',
-            port: 3306,
-            user: 'ryc',
-            password: 'KiYap3E8X8RLcM6T',
-            database: 'ryc',
-            connectTimeout: 60000,
-            acquireTimeout: 60000,
-            timeout: 60000
-        });
-
-        console.log('鏁版嵁搴撹繛鎺ユ垚鍔燂紒');
-
-        // 鏌ヨ娲诲姩鏁版嵁
-        const [rows] = await connection.execute(`
-            SELECT 
-                id,
-                name,
-                signup_deadline,
-                match_time,
-                address,
-                player_max,
-                state,
-                create_time,
-                update_time
-            FROM t_activity 
-            WHERE state = 1 
-            ORDER BY id DESC 
-            LIMIT 10
-        `);
-
-        console.log('\n=== 娲诲姩鏁版嵁妫�鏌� ===');
-        console.log(`鎵惧埌 ${rows.length} 鏉℃椿鍔ㄨ褰曪細\n`);
-
-        rows.forEach((row, index) => {
-            console.log(`${index + 1}. 娲诲姩ID: ${row.id}`);
-            console.log(`   鍚嶇О: ${row.name}`);
-            console.log(`   鎶ュ悕鎴鏃堕棿: ${row.signup_deadline || '鏈缃�'}`);
-            console.log(`   姣旇禌鏃堕棿: ${row.match_time || '鏈缃�'}`);
-            console.log(`   鍦板潃: ${row.address || '鏈缃�'}`);
-            console.log(`   鏈�澶т汉鏁�: ${row.player_max || '鏈檺鍒�'}`);
-            console.log(`   鐘舵��: ${row.state}`);
-            console.log(`   鍒涘缓鏃堕棿: ${row.create_time}`);
-            console.log(`   鏇存柊鏃堕棿: ${row.update_time}`);
-            console.log('   ---');
-        });
-
-        // 缁熻鏃堕棿瀛楁涓虹┖鐨勮褰�
-        const [emptyDates] = await connection.execute(`
-            SELECT 
-                COUNT(*) as total,
-                SUM(CASE WHEN signup_deadline IS NULL THEN 1 ELSE 0 END) as empty_signup_deadline,
-                SUM(CASE WHEN match_time IS NULL THEN 1 ELSE 0 END) as empty_match_time
-            FROM t_activity 
-            WHERE state = 1
-        `);
-
-        console.log('\n=== 鏃堕棿瀛楁缁熻 ===');
-        console.log(`鎬绘椿鍔ㄦ暟: ${emptyDates[0].total}`);
-        console.log(`鎶ュ悕鎴鏃堕棿涓虹┖: ${emptyDates[0].empty_signup_deadline}`);
-        console.log(`姣旇禌鏃堕棿涓虹┖: ${emptyDates[0].empty_match_time}`);
-
-        // 妫�鏌ユ渶杩戠殑娲诲姩璇︽儏
-        const [latestActivity] = await connection.execute(`
-            SELECT * FROM t_activity WHERE state = 1 ORDER BY id DESC LIMIT 1
-        `);
-
-        if (latestActivity.length > 0) {
-            console.log('\n=== 鏈�鏂版椿鍔ㄨ鎯� ===');
-            const activity = latestActivity[0];
-            console.log('瀹屾暣娲诲姩淇℃伅:');
-            console.log(JSON.stringify(activity, null, 2));
-        }
-
-    } catch (error) {
-        console.error('鏁版嵁搴撴煡璇㈠け璐�:', error.message);
-        console.error('閿欒璇︽儏:', error);
-    } finally {
-        if (connection) {
-            await connection.end();
-            console.log('\n鏁版嵁搴撹繛鎺ュ凡鍏抽棴');
-        }
-    }
-}
-
-// 杩愯鏌ヨ
-checkActivityDates();
\ No newline at end of file
diff --git a/check_login_records.js b/check_login_records.js
deleted file mode 100644
index df8f183..0000000
--- a/check_login_records.js
+++ /dev/null
@@ -1,99 +0,0 @@
-const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
-
-// 閰嶇疆
-const GRAPHQL_URL = 'http://localhost:8080/api/graphql';
-
-// 妫�鏌ョ櫥褰曡褰曠殑GraphQL鏌ヨ
-const CHECK_LOGIN_RECORDS_QUERY = `
-  query {
-    loginRecords: getAllLoginRecords {
-      id
-      userId
-      wxOpenid
-      wxSessionKey
-      loginTime
-      loginIp
-      deviceInfo
-      user {
-        id
-        username
-        phone
-      }
-    }
-  }
-`;
-
-async function checkLoginRecords() {
-  console.log('馃攳 妫�鏌ユ暟鎹簱涓殑鐧诲綍璁板綍...\n');
-  
-  try {
-    const response = await fetch(GRAPHQL_URL, {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-      },
-      body: JSON.stringify({
-        query: CHECK_LOGIN_RECORDS_QUERY
-      })
-    });
-
-    const result = await response.json();
-    
-    if (result.errors) {
-      console.log('鉂� GraphQL鏌ヨ澶辫触:', result.errors[0].message);
-      return;
-    }
-
-    const records = result.data.loginRecords;
-    console.log(`馃搳 鎵惧埌 ${records.length} 鏉$櫥褰曡褰�:\n`);
-    
-    records.forEach((record, index) => {
-      console.log(`璁板綍 ${index + 1}:`);
-      console.log(`  - ID: ${record.id}`);
-      console.log(`  - 鐢ㄦ埛ID: ${record.userId}`);
-      console.log(`  - 寰俊OpenID: ${record.wxOpenid}`);
-      console.log(`  - SessionKey: ${record.wxSessionKey ? record.wxSessionKey.substring(0, 10) + '...' : '鏃�'}`);
-      console.log(`  - 鐧诲綍鏃堕棿: ${record.loginTime}`);
-      console.log(`  - 鐧诲綍IP: ${record.loginIp}`);
-      console.log(`  - 璁惧淇℃伅: ${record.deviceInfo}`);
-      if (record.user) {
-        console.log(`  - 鐢ㄦ埛鍚�: ${record.user.username}`);
-        console.log(`  - 鎵嬫満鍙�: ${record.user.phone || '鏈缃�'}`);
-      }
-      console.log('');
-    });
-
-    // 鍒嗘瀽鏁版嵁
-    const testDataRecords = records.filter(r => 
-      r.wxOpenid && (r.wxOpenid.includes('test') || r.wxOpenid.includes('mock'))
-    );
-    
-    const realDataRecords = records.filter(r => 
-      r.wxOpenid && !r.wxOpenid.includes('test') && !r.wxOpenid.includes('mock')
-    );
-
-    console.log('馃搱 鏁版嵁鍒嗘瀽:');
-    console.log(`  - 娴嬭瘯鏁版嵁璁板綍: ${testDataRecords.length} 鏉);
-    console.log(`  - 鐪熷疄鏁版嵁璁板綍: ${realDataRecords.length} 鏉);
-    
-    if (testDataRecords.length > 0) {
-      console.log('\n鈿狅笍  鍙戠幇娴嬭瘯鏁版嵁璁板綍:');
-      testDataRecords.forEach(record => {
-        console.log(`  - OpenID: ${record.wxOpenid}`);
-      });
-    }
-    
-    if (realDataRecords.length > 0) {
-      console.log('\n鉁� 鍙戠幇鐪熷疄鏁版嵁璁板綍:');
-      realDataRecords.forEach(record => {
-        console.log(`  - OpenID: ${record.wxOpenid.substring(0, 10)}...`);
-      });
-    }
-
-  } catch (error) {
-    console.error('鉂� 妫�鏌ョ櫥褰曡褰曞け璐�:', error.message);
-  }
-}
-
-// 杩愯妫�鏌�
-checkLoginRecords();
\ No newline at end of file
diff --git a/clear_tables.sql b/clear_tables.sql
new file mode 100644
index 0000000..e9ab42b
--- /dev/null
+++ b/clear_tables.sql
@@ -0,0 +1,42 @@
+-- 娓呯┖鎸囧畾琛ㄧ殑鏁版嵁
+-- 鎸夌収澶栭敭渚濊禆鍏崇郴鐨勯『搴忓垹闄�
+
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- 娓呯┖璇勫垎鐩稿叧琛�
+DELETE FROM t_activity_rating_item;
+DELETE FROM t_activity_rating;
+
+-- 娓呯┖娲诲姩閫夋墜琛�
+DELETE FROM t_activity_player;
+
+-- 娓呯┖娲诲姩璇勫琛�
+DELETE FROM t_activity_judge;
+
+-- 娓呯┖閫夋墜琛�
+DELETE FROM t_player;
+
+-- 娓呯┖娲诲姩琛�
+DELETE FROM t_activity;
+
+SET FOREIGN_KEY_CHECKS = 1;
+
+-- 楠岃瘉娓呯┖缁撴灉
+SELECT 'Tables cleared successfully' as result;
+SELECT 
+    't_activity' as table_name, COUNT(*) as record_count FROM t_activity
+UNION ALL
+SELECT 
+    't_activity_player' as table_name, COUNT(*) as record_count FROM t_activity_player
+UNION ALL
+SELECT 
+    't_activity_judge' as table_name, COUNT(*) as record_count FROM t_activity_judge
+UNION ALL
+SELECT 
+    't_activity_rating' as table_name, COUNT(*) as record_count FROM t_activity_rating
+UNION ALL
+SELECT 
+    't_activity_rating_item' as table_name, COUNT(*) as record_count FROM t_activity_rating_item
+UNION ALL
+SELECT 
+    't_player' as table_name, COUNT(*) as record_count FROM t_player;
\ No newline at end of file
diff --git a/db.sql b/db.sql
index b67c704..7b2b5b0 100644
--- a/db.sql
+++ b/db.sql
@@ -1,7 +1,16 @@
--- Database schema for ryc
--- Generated at: 2025/9/27 19:34:25
+-- 鏁版嵁搴撶粨鏋勫鍑�
+-- 鏁版嵁搴�: ryc
+-- 瀵煎嚭鏃堕棿: 2025/9/30 08:39:43
+-- 
+-- 娉ㄦ剰锛氭鏂囦欢浠呭寘鍚〃缁撴瀯锛屼笉鍖呭惈鏁版嵁
 
--- Table: t_activity
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for t_activity
+-- ----------------------------
+DROP TABLE IF EXISTS `t_activity`;
 CREATE TABLE `t_activity` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `pid` bigint NOT NULL DEFAULT '0',
@@ -23,9 +32,12 @@
   KEY `fk_t_activity_rating_scheme` (`rating_scheme_id`) USING BTREE,
   KEY `idx_t_activity_deadline` (`signup_deadline`) USING BTREE,
   CONSTRAINT `fk_t_activity_rating_scheme` FOREIGN KEY (`rating_scheme_id`) REFERENCES `t_rating_scheme` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
-) ENGINE=InnoDB AUTO_INCREMENT=62 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=76 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_activity_judge
+-- ----------------------------
+-- Table structure for t_activity_judge
+-- ----------------------------
+DROP TABLE IF EXISTS `t_activity_judge`;
 CREATE TABLE `t_activity_judge` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `activity_id` bigint NOT NULL,
@@ -40,9 +52,12 @@
   `version` bigint NOT NULL DEFAULT '0',
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE KEY `uq_stage_judge` (`stage_id`,`judge_id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_activity_player
+-- ----------------------------
+-- Table structure for t_activity_player
+-- ----------------------------
+DROP TABLE IF EXISTS `t_activity_player`;
 CREATE TABLE `t_activity_player` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `activity_id` bigint NOT NULL,
@@ -63,9 +78,12 @@
   `update_user_id` bigint DEFAULT NULL,
   `version` bigint NOT NULL DEFAULT '0',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_activity_player_rating
+-- ----------------------------
+-- Table structure for t_activity_player_rating
+-- ----------------------------
+DROP TABLE IF EXISTS `t_activity_player_rating`;
 CREATE TABLE `t_activity_player_rating` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `activity_id` bigint NOT NULL,
@@ -85,7 +103,10 @@
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
 
--- Table: t_activity_player_rating_item
+-- ----------------------------
+-- Table structure for t_activity_player_rating_item
+-- ----------------------------
+DROP TABLE IF EXISTS `t_activity_player_rating_item`;
 CREATE TABLE `t_activity_player_rating_item` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `activity_id` bigint NOT NULL,
@@ -107,7 +128,10 @@
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_carousel
+-- ----------------------------
+-- Table structure for t_carousel
+-- ----------------------------
+DROP TABLE IF EXISTS `t_carousel`;
 CREATE TABLE `t_carousel` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -122,7 +146,10 @@
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='杞挱鍥�';
 
--- Table: t_employee
+-- ----------------------------
+-- Table structure for t_employee
+-- ----------------------------
+DROP TABLE IF EXISTS `t_employee`;
 CREATE TABLE `t_employee` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -138,9 +165,12 @@
   `description` varchar(255) DEFAULT NULL,
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE KEY `phone` (`phone`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_employee_role
+-- ----------------------------
+-- Table structure for t_employee_role
+-- ----------------------------
+DROP TABLE IF EXISTS `t_employee_role`;
 CREATE TABLE `t_employee_role` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `employee_id` bigint NOT NULL,
@@ -154,7 +184,10 @@
   KEY `fk_t_user_role_role` (`role_id`) USING BTREE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_judge
+-- ----------------------------
+-- Table structure for t_judge
+-- ----------------------------
+DROP TABLE IF EXISTS `t_judge`;
 CREATE TABLE `t_judge` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -174,9 +207,12 @@
   `introduction` text COMMENT '涓汉浠嬬粛',
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE KEY `phone` (`phone`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=68 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_judge_tag
+-- ----------------------------
+-- Table structure for t_judge_tag
+-- ----------------------------
+DROP TABLE IF EXISTS `t_judge_tag`;
 CREATE TABLE `t_judge_tag` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `judge_id` bigint NOT NULL,
@@ -189,9 +225,12 @@
   `version` bigint NOT NULL DEFAULT '0',
   PRIMARY KEY (`id`) USING BTREE,
   KEY `fk_t_judge_major_tag` (`tag_id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=76 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=97 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_media
+-- ----------------------------
+-- Table structure for t_media
+-- ----------------------------
+DROP TABLE IF EXISTS `t_media`;
 CREATE TABLE `t_media` (
   `id` int NOT NULL AUTO_INCREMENT,
   `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
@@ -210,10 +249,62 @@
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   `update_user_id` bigint DEFAULT NULL,
   `version` bigint NOT NULL DEFAULT '0',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `uq_type_id` (`target_type`,`target_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=116 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_notification_task
+-- ----------------------------
+-- Table structure for t_media_backup_avatar_migration
+-- ----------------------------
+DROP TABLE IF EXISTS `t_media_backup_avatar_migration`;
+CREATE TABLE `t_media_backup_avatar_migration` (
+  `id` int NOT NULL DEFAULT '0',
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
+  `target_type` int NOT NULL,
+  `target_id` bigint NOT NULL,
+  `media_type` int NOT NULL,
+  `path` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '鑵捐浜戠殑瀛樺偍妗跺湴鍧�',
+  `thumb_path` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
+  `file_ext` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
+  `file_size` int NOT NULL,
+  `duration` int DEFAULT NULL COMMENT '瑙嗛鐨勯暱搴︾',
+  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
+  `state` int NOT NULL DEFAULT '1',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `create_user_id` bigint DEFAULT NULL,
+  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `update_user_id` bigint DEFAULT NULL,
+  `version` bigint NOT NULL DEFAULT '0'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- ----------------------------
+-- Table structure for t_msg
+-- ----------------------------
+DROP TABLE IF EXISTS `t_msg`;
+CREATE TABLE `t_msg` (
+  `id` int NOT NULL AUTO_INCREMENT,
+  `target_type` int NOT NULL,
+  `target_id` int NOT NULL,
+  `player_id` int NOT NULL,
+  `user_id` int NOT NULL,
+  `content` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `template_content` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL,
+  `wx_msg_success` bit(1) NOT NULL,
+  `wx_msg_err_count` int NOT NULL DEFAULT '0',
+  `wx_msg_last_err` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+  `state` int NOT NULL COMMENT '0:鏆傛椂涓嶅彂甯冿紝 1锛氬彲浠ュ彂甯冿紝2锛氬凡缁忓彂甯�',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `create_user_id` bigint DEFAULT NULL,
+  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `update_user_id` bigint DEFAULT NULL,
+  `version` bigint NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- ----------------------------
+-- Table structure for t_notification_task
+-- ----------------------------
+DROP TABLE IF EXISTS `t_notification_task`;
 CREATE TABLE `t_notification_task` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -233,7 +324,10 @@
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_permission
+-- ----------------------------
+-- Table structure for t_permission
+-- ----------------------------
+DROP TABLE IF EXISTS `t_permission`;
 CREATE TABLE `t_permission` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `code` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -251,7 +345,10 @@
   KEY `idx_t_permission_code` (`code`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_player
+-- ----------------------------
+-- Table structure for t_player
+-- ----------------------------
+DROP TABLE IF EXISTS `t_player`;
 CREATE TABLE `t_player` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
@@ -272,9 +369,12 @@
   `user_id` bigint NOT NULL,
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE KEY `phone` (`phone`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_rating_item
+-- ----------------------------
+-- Table structure for t_rating_item
+-- ----------------------------
+DROP TABLE IF EXISTS `t_rating_item`;
 CREATE TABLE `t_rating_item` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `scheme_id` bigint NOT NULL,
@@ -288,11 +388,13 @@
   `update_user_id` bigint DEFAULT NULL,
   `version` bigint NOT NULL DEFAULT '0',
   PRIMARY KEY (`id`) USING BTREE,
-  KEY `idx_t_rating_item_scheme` (`scheme_id`) USING BTREE,
-  CONSTRAINT `fk_t_rating_item_scheme` FOREIGN KEY (`scheme_id`) REFERENCES `t_rating_scheme` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
-) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+  KEY `idx_t_rating_item_scheme` (`scheme_id`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_rating_scheme
+-- ----------------------------
+-- Table structure for t_rating_scheme
+-- ----------------------------
+DROP TABLE IF EXISTS `t_rating_scheme`;
 CREATE TABLE `t_rating_scheme` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -304,9 +406,12 @@
   `update_user_id` bigint DEFAULT NULL,
   `version` bigint NOT NULL DEFAULT '0',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_region
+-- ----------------------------
+-- Table structure for t_region
+-- ----------------------------
+DROP TABLE IF EXISTS `t_region`;
 CREATE TABLE `t_region` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `pid` bigint NOT NULL COMMENT '鑷叧鑱�',
@@ -323,9 +428,12 @@
   `version` bigint NOT NULL DEFAULT '0',
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE KEY `code` (`code`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=142 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_role
+-- ----------------------------
+-- Table structure for t_role
+-- ----------------------------
+DROP TABLE IF EXISTS `t_role`;
 CREATE TABLE `t_role` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -342,7 +450,10 @@
   KEY `idx_t_role_code` (`code`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_role_permission
+-- ----------------------------
+-- Table structure for t_role_permission
+-- ----------------------------
+DROP TABLE IF EXISTS `t_role_permission`;
 CREATE TABLE `t_role_permission` (
   `id` int NOT NULL AUTO_INCREMENT,
   `role_id` bigint NOT NULL,
@@ -356,7 +467,10 @@
   KEY `fk_t_role_permission_perm` (`permission_id`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_tag
+-- ----------------------------
+-- Table structure for t_tag
+-- ----------------------------
+DROP TABLE IF EXISTS `t_tag`;
 CREATE TABLE `t_tag` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -374,7 +488,10 @@
   KEY `idx_t_tag_category` (`category`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_user
+-- ----------------------------
+-- Table structure for t_user
+-- ----------------------------
+DROP TABLE IF EXISTS `t_user`;
 CREATE TABLE `t_user` (
   `id` bigint NOT NULL AUTO_INCREMENT,
   `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
@@ -395,9 +512,12 @@
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE KEY `uq_wx_open_id` (`wx_openid`) USING BTREE,
   UNIQUE KEY `uq_phone` (`phone`)
-) ENGINE=InnoDB AUTO_INCREMENT=62 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=133 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
--- Table: t_wx_login_record
+-- ----------------------------
+-- Table structure for t_wx_login_record
+-- ----------------------------
+DROP TABLE IF EXISTS `t_wx_login_record`;
 CREATE TABLE `t_wx_login_record` (
   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
   `wx_openid` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '寰俊openid',
@@ -411,12 +531,18 @@
   `phone_auth_time` datetime DEFAULT NULL COMMENT '鎵嬫満鍙锋巿鏉冩椂闂�',
   `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
   `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
-  `state` tinyint(1) DEFAULT '1' COMMENT '鐘舵�侊細0-绂佺敤锛�1-鍚敤',
+  `state` int DEFAULT '1' COMMENT '鐘舵�侊細0-绂佺敤锛�1-鍚敤',
+  `create_user_id` bigint DEFAULT NULL COMMENT '鍒涘缓鐢ㄦ埛ID',
+  `update_user_id` bigint DEFAULT NULL COMMENT '鏇存柊鐢ㄦ埛ID',
+  `version` bigint NOT NULL DEFAULT '0' COMMENT '鐗堟湰鍙�',
   PRIMARY KEY (`id`),
   KEY `idx_wx_openid` (`wx_openid`),
   KEY `idx_wx_unionid` (`wx_unionid`),
   KEY `idx_user_id` (`user_id`),
   KEY `idx_login_time` (`login_time`),
-  KEY `idx_phone_authorized` (`phone_authorized`)
-) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='寰俊鐧诲綍璁板綍琛�';
+  KEY `idx_phone_authorized` (`phone_authorized`),
+  KEY `idx_create_user_id` (`create_user_id`),
+  KEY `idx_update_user_id` (`update_user_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=116 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='寰俊鐧诲綍璁板綍琛�';
 
+SET FOREIGN_KEY_CHECKS = 1;
diff --git a/debug_phone_error.js b/debug_phone_error.js
deleted file mode 100644
index 562ac0f..0000000
--- a/debug_phone_error.js
+++ /dev/null
@@ -1,158 +0,0 @@
-const axios = require('axios');
-
-async function debugPhoneError() {
-    console.log('馃攳 璇婃柇鎵嬫満鍙疯幏鍙朅PI閿欒');
-    console.log('='.repeat(50));
-    
-    const backendUrl = 'http://localhost:8080/api/graphql';
-    
-    // 娴嬭瘯1: 妫�鏌ユ柊鐗圓PI鐨勮缁嗛敊璇�
-    console.log('\n1锔忊儯 娴嬭瘯鏂扮増API (getPhoneNumberByCode)');
-    try {
-        const newApiResponse = await axios.post(backendUrl, {
-            query: `
-                mutation GetPhoneNumberByCode($code: String!) {
-                    getPhoneNumberByCode(code: $code) {
-                        phoneNumber
-                        purePhoneNumber
-                        countryCode
-                    }
-                }
-            `,
-            variables: { code: "test_code_from_wechat" }
-        }, {
-            headers: { 'Content-Type': 'application/json' }
-        });
-        
-        console.log('鍝嶅簲鐘舵��:', newApiResponse.status);
-        console.log('鍝嶅簲鏁版嵁:', JSON.stringify(newApiResponse.data, null, 2));
-        
-        if (newApiResponse.data.errors) {
-            console.log('\n鉂� 鏂扮増API閿欒璇︽儏:');
-            newApiResponse.data.errors.forEach((error, index) => {
-                console.log(`閿欒 ${index + 1}:`, error.message);
-                if (error.extensions) {
-                    console.log('鎵╁睍淇℃伅:', error.extensions);
-                }
-            });
-        }
-        
-    } catch (error) {
-        console.error('鉂� 鏂扮増API璇锋眰澶辫触:', error.message);
-        if (error.response) {
-            console.log('閿欒鍝嶅簲:', error.response.data);
-        }
-    }
-    
-    // 娴嬭瘯2: 妫�鏌ユ棫鐗圓PI鐨勮缁嗛敊璇�
-    console.log('\n2锔忊儯 娴嬭瘯鏃х増API (decryptPhoneNumber)');
-    try {
-        const oldApiResponse = await axios.post(backendUrl, {
-            query: `
-                mutation DecryptPhoneNumber($encryptedData: String!, $iv: String!, $sessionKey: String!) {
-                    decryptPhoneNumber(encryptedData: $encryptedData, iv: $iv, sessionKey: $sessionKey) {
-                        phoneNumber
-                        purePhoneNumber
-                        countryCode
-                    }
-                }
-            `,
-            variables: {
-                encryptedData: "test_encrypted_data",
-                iv: "test_iv_value",
-                sessionKey: "test_session_key"
-            }
-        }, {
-            headers: { 'Content-Type': 'application/json' }
-        });
-        
-        console.log('鍝嶅簲鐘舵��:', oldApiResponse.status);
-        console.log('鍝嶅簲鏁版嵁:', JSON.stringify(oldApiResponse.data, null, 2));
-        
-        if (oldApiResponse.data.errors) {
-            console.log('\n鉂� 鏃х増API閿欒璇︽儏:');
-            oldApiResponse.data.errors.forEach((error, index) => {
-                console.log(`閿欒 ${index + 1}:`, error.message);
-                if (error.extensions) {
-                    console.log('鎵╁睍淇℃伅:', error.extensions);
-                }
-            });
-        }
-        
-    } catch (error) {
-        console.error('鉂� 鏃х増API璇锋眰澶辫触:', error.message);
-        if (error.response) {
-            console.log('閿欒鍝嶅簲:', error.response.data);
-        }
-    }
-    
-    // 娴嬭瘯3: 妫�鏌ュ井淇PI閰嶇疆
-    console.log('\n3锔忊儯 妫�鏌ュ彲鑳界殑閰嶇疆闂');
-    console.log('甯歌閿欒鍘熷洜:');
-    console.log('- 寰俊AppSecret鏈厤缃垨閰嶇疆閿欒');
-    console.log('- 寰俊API璋冪敤棰戠巼闄愬埗');
-    console.log('- code鍙傛暟鏃犳晥鎴栧凡杩囨湡');
-    console.log('- access_token鑾峰彇澶辫触');
-    console.log('- 缃戠粶杩炴帴闂');
-    
-    // 娴嬭瘯4: 妯℃嫙鐪熷疄鐨勫皬绋嬪簭璋冪敤
-    console.log('\n4锔忊儯 妯℃嫙灏忕▼搴忚皟鐢ㄥ満鏅�');
-    
-    // 鍦烘櫙1: 鍙湁code鍙傛暟锛堟柊鐗圓PI锛�
-    console.log('\n鍦烘櫙1: 鍙湁code鍙傛暟');
-    try {
-        const scenario1 = await axios.post(backendUrl, {
-            query: `
-                mutation GetPhoneNumberByCode($code: String!) {
-                    getPhoneNumberByCode(code: $code) {
-                        phoneNumber
-                        purePhoneNumber
-                        countryCode
-                    }
-                }
-            `,
-            variables: { code: "" } // 绌篶ode
-        }, {
-            headers: { 'Content-Type': 'application/json' }
-        });
-        
-        if (scenario1.data.errors) {
-            console.log('绌篶ode閿欒:', scenario1.data.errors[0].message);
-        }
-    } catch (error) {
-        console.log('绌篶ode娴嬭瘯閿欒:', error.message);
-    }
-    
-    // 鍦烘櫙2: 缂哄皯蹇呰鍙傛暟
-    console.log('\n鍦烘櫙2: 缂哄皯蹇呰鍙傛暟');
-    try {
-        const scenario2 = await axios.post(backendUrl, {
-            query: `
-                mutation GetPhoneNumberByCode {
-                    getPhoneNumberByCode {
-                        phoneNumber
-                        purePhoneNumber
-                        countryCode
-                    }
-                }
-            `
-        }, {
-            headers: { 'Content-Type': 'application/json' }
-        });
-        
-        if (scenario2.data.errors) {
-            console.log('缂哄皯鍙傛暟閿欒:', scenario2.data.errors[0].message);
-        }
-    } catch (error) {
-        console.log('缂哄皯鍙傛暟娴嬭瘯閿欒:', error.message);
-    }
-    
-    console.log('\n馃敡 寤鸿鐨勮В鍐虫柟妗�:');
-    console.log('1. 妫�鏌ュ井淇″皬绋嬪簭鐨凙ppSecret鐜鍙橀噺閰嶇疆');
-    console.log('2. 纭浼犲叆鐨刢ode鍙傛暟鏄湁鏁堢殑寰俊杩斿洖鍊�');
-    console.log('3. 妫�鏌ョ綉缁滆繛鎺ュ拰寰俊API鏈嶅姟鐘舵��');
-    console.log('4. 鏌ョ湅鍚庣鏃ュ織鑾峰彇鏇磋缁嗙殑閿欒淇℃伅');
-    console.log('5. 楠岃瘉寰俊灏忕▼搴忕殑鍩虹搴撶増鏈拰API鏉冮檺');
-}
-
-debugPhoneError().catch(console.error);
\ No newline at end of file
diff --git a/fix_wx_login_table.js b/fix_wx_login_table.js
deleted file mode 100644
index 0f38476..0000000
--- a/fix_wx_login_table.js
+++ /dev/null
@@ -1,154 +0,0 @@
-const mysql = require('mysql2/promise');
-
-async function fixWxLoginTable() {
-    let connection;
-    
-    try {
-        // 鍒涘缓鏁版嵁搴撹繛鎺�
-        connection = await mysql.createConnection({
-            host: '139.155.104.10',
-            port: 3306,
-            user: 'ryc',
-            password: 'KiYap3E8X8RLcM6T',
-            database: 'ryc'
-        });
-        
-        console.log('鉁� 鏁版嵁搴撹繛鎺ユ垚鍔�');
-        
-        // 妫�鏌ヨ〃鏄惁瀛樺湪
-        const [tables] = await connection.execute(
-            "SHOW TABLES LIKE 't_wx_login_record'"
-        );
-        
-        if (tables.length === 0) {
-            console.log('鉂� 琛� t_wx_login_record 涓嶅瓨鍦�');
-            return;
-        }
-        
-        console.log('鉁� 琛� t_wx_login_record 瀛樺湪');
-        
-        // 妫�鏌ュ綋鍓嶈〃缁撴瀯
-        const [columns] = await connection.execute(
-            "DESCRIBE t_wx_login_record"
-        );
-        
-        console.log('褰撳墠琛ㄧ粨鏋�:');
-        columns.forEach(col => {
-            console.log(`  - ${col.Field}: ${col.Type} ${col.Null === 'YES' ? 'NULL' : 'NOT NULL'} ${col.Default ? `DEFAULT ${col.Default}` : ''}`);
-        });
-        
-        // 妫�鏌ラ渶瑕佹坊鍔犵殑瀛楁
-        const existingColumns = columns.map(col => col.Field);
-        const fieldsToAdd = [];
-        
-        if (!existingColumns.includes('create_user_id')) {
-            fieldsToAdd.push({
-                name: 'create_user_id',
-                sql: 'ADD COLUMN create_user_id BIGINT NULL COMMENT \'鍒涘缓鐢ㄦ埛ID\''
-            });
-        }
-        
-        if (!existingColumns.includes('update_user_id')) {
-            fieldsToAdd.push({
-                name: 'update_user_id', 
-                sql: 'ADD COLUMN update_user_id BIGINT NULL COMMENT \'鏇存柊鐢ㄦ埛ID\''
-            });
-        }
-        
-        if (!existingColumns.includes('version')) {
-            fieldsToAdd.push({
-                name: 'version',
-                sql: 'ADD COLUMN version BIGINT NOT NULL DEFAULT 0 COMMENT \'鐗堟湰鍙穃''
-            });
-        }
-        
-        if (fieldsToAdd.length === 0) {
-            console.log('鉁� 鎵�鏈夊繀闇�瀛楁閮藉凡瀛樺湪锛屾棤闇�淇敼');
-            return;
-        }
-        
-        console.log(`闇�瑕佹坊鍔� ${fieldsToAdd.length} 涓瓧娈�:`);
-        fieldsToAdd.forEach(field => {
-            console.log(`  - ${field.name}`);
-        });
-        
-        // 娣诲姞瀛楁
-        for (const field of fieldsToAdd) {
-            console.log(`姝e湪娣诲姞瀛楁: ${field.name}...`);
-            
-            try {
-                await connection.execute(`ALTER TABLE t_wx_login_record ${field.sql}`);
-                console.log(`鉁� 鎴愬姛娣诲姞瀛楁: ${field.name}`);
-            } catch (error) {
-                console.error(`鉂� 娣诲姞瀛楁 ${field.name} 澶辫触:`, error.message);
-                throw error;
-            }
-        }
-        
-        // 娣诲姞绱㈠紩
-        console.log('姝e湪娣诲姞绱㈠紩...');
-        
-        try {
-            // 妫�鏌ョ储寮曟槸鍚﹀凡瀛樺湪
-            const [indexes] = await connection.execute(
-                "SHOW INDEX FROM t_wx_login_record WHERE Key_name = 'idx_create_user_id'"
-            );
-            
-            if (indexes.length === 0) {
-                await connection.execute(
-                    'CREATE INDEX idx_create_user_id ON t_wx_login_record(create_user_id)'
-                );
-                console.log('鉁� 鎴愬姛娣诲姞 create_user_id 绱㈠紩');
-            } else {
-                console.log('鉁� create_user_id 绱㈠紩宸插瓨鍦�');
-            }
-        } catch (error) {
-            console.log('鈿狅笍 娣诲姞 create_user_id 绱㈠紩澶辫触:', error.message);
-        }
-        
-        try {
-            const [indexes] = await connection.execute(
-                "SHOW INDEX FROM t_wx_login_record WHERE Key_name = 'idx_update_user_id'"
-            );
-            
-            if (indexes.length === 0) {
-                await connection.execute(
-                    'CREATE INDEX idx_update_user_id ON t_wx_login_record(update_user_id)'
-                );
-                console.log('鉁� 鎴愬姛娣诲姞 update_user_id 绱㈠紩');
-            } else {
-                console.log('鉁� update_user_id 绱㈠紩宸插瓨鍦�');
-            }
-        } catch (error) {
-            console.log('鈿狅笍 娣诲姞 update_user_id 绱㈠紩澶辫触:', error.message);
-        }
-        
-        // 楠岃瘉淇敼缁撴灉
-        console.log('楠岃瘉淇敼缁撴灉...');
-        const [newColumns] = await connection.execute(
-            "DESCRIBE t_wx_login_record"
-        );
-        
-        console.log('淇敼鍚庣殑琛ㄧ粨鏋�:');
-        newColumns.forEach(col => {
-            console.log(`  - ${col.Field}: ${col.Type} ${col.Null === 'YES' ? 'NULL' : 'NOT NULL'} ${col.Default !== null ? `DEFAULT ${col.Default}` : ''}`);
-        });
-        
-        console.log('鉁� 琛ㄧ粨鏋勪慨澶嶅畬鎴愶紒');
-        
-    } catch (error) {
-        console.error('鉂� 淇琛ㄧ粨鏋勬椂鍙戠敓閿欒:', error);
-        throw error;
-    } finally {
-        if (connection) {
-            await connection.end();
-            console.log('鏁版嵁搴撹繛鎺ュ凡鍏抽棴');
-        }
-    }
-}
-
-// 杩愯淇鑴氭湰
-fixWxLoginTable().catch(error => {
-    console.error('鑴氭湰鎵ц澶辫触:', error);
-    process.exit(1);
-});
\ No newline at end of file
diff --git a/t_media.sql b/t_media.sql
new file mode 100644
index 0000000..89bcc10
--- /dev/null
+++ b/t_media.sql
@@ -0,0 +1,57 @@
+/*
+ Navicat Premium Dump SQL
+
+ Source Server         : 钃夋槗鍒�
+ Source Server Type    : MySQL
+ Source Server Version : 80405 (8.4.5)
+ Source Host           : 139.155.104.10:3306
+ Source Schema         : ryc
+
+ Target Server Type    : MySQL
+ Target Server Version : 80405 (8.4.5)
+ File Encoding         : 65001
+
+ Date: 30/09/2025 09:55:45
+*/
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for t_media
+-- ----------------------------
+DROP TABLE IF EXISTS `t_media`;
+CREATE TABLE `t_media`  (
+  `id` int NOT NULL AUTO_INCREMENT,
+  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
+  `target_type` int NOT NULL,
+  `target_id` bigint NOT NULL,
+  `media_type` int NOT NULL,
+  `path` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '鑵捐浜戠殑瀛樺偍妗跺湴鍧�',
+  `thumb_path` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
+  `file_ext` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
+  `file_size` int NOT NULL,
+  `duration` int NULL DEFAULT NULL COMMENT '瑙嗛鐨勯暱搴︾',
+  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
+  `state` int NOT NULL DEFAULT 1,
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `create_user_id` bigint NULL DEFAULT NULL,
+  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `update_user_id` bigint NULL DEFAULT NULL,
+  `version` bigint NOT NULL DEFAULT 0,
+  PRIMARY KEY (`id`) USING BTREE,
+  INDEX `uq_type_id`(`target_type` ASC, `target_id` ASC) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 119 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of t_media
+-- ----------------------------
+INSERT INTO `t_media` VALUES (112, 'judge_avatar.jpg', 1, 65, 1, 'avatars/judge_avatar_1759142940287.jpg', NULL, 'jpg', 1024, NULL, NULL, 1, '2025-09-29 18:48:59', NULL, '2025-09-29 18:48:59', NULL, 0);
+INSERT INTO `t_media` VALUES (113, 'logo.jpg', 1, 66, 1, '20250929/b5840d47-b046-4461-9d78-27881c917f03.jpg', NULL, 'jpg', 67671, NULL, NULL, 1, '2025-09-29 18:49:02', NULL, '2025-09-29 18:49:02', NULL, 0);
+INSERT INTO `t_media` VALUES (114, 'a1.png', 2, 73, 1, '20250929/bc8be85a-c0d9-4d75-885e-cb19d613e00f.png', NULL, 'png', 3628657, NULL, NULL, 1, '2025-09-29 19:16:11', NULL, '2025-09-29 19:16:11', NULL, 0);
+INSERT INTO `t_media` VALUES (115, 'bug1.png', 2, 73, 1, '20250929/23aee0a8-dc1e-4ec5-b4a6-2b80370940cb.png', NULL, 'png', 84959, NULL, NULL, 1, '2025-09-29 19:16:11', NULL, '2025-09-29 19:16:11', NULL, 0);
+INSERT INTO `t_media` VALUES (116, 'mg5v05lk-vchro9dh-dn24k81r-ibqm.jpg', 7, 2, 1, 'avatars/20250930/mg5v05lk-vchro9dh-dn24k81r-ibqm.jpg', NULL, 'jpg', 3624223, NULL, NULL, 1, '2025-09-30 09:08:55', NULL, '2025-09-30 09:08:55', NULL, 0);
+INSERT INTO `t_media` VALUES (117, 'mg5v06il-rei95zgu-f9okwt82-iruh.png', 5, 51, 1, 'attachments/20250930/mg5v06il-rei95zgu-f9okwt82-iruh.png', NULL, 'png', 93686, NULL, NULL, 1, '2025-09-30 09:08:55', NULL, '2025-09-30 09:08:55', NULL, 0);
+INSERT INTO `t_media` VALUES (118, 'test-avatar.jpg', 7, 1, 1, 'avatars/test-avatar.jpg', NULL, 'jpg', 50000, NULL, NULL, 1, '2025-09-30 09:32:13', NULL, '2025-09-30 09:32:13', NULL, 0);
+
+SET FOREIGN_KEY_CHECKS = 1;
diff --git a/verify_birthday_data.js b/verify_birthday_data.js
deleted file mode 100644
index b40c20a..0000000
--- a/verify_birthday_data.js
+++ /dev/null
@@ -1,86 +0,0 @@
-const mysql = require('mysql2/promise');
-
-// 鏁版嵁搴撹繛鎺ラ厤缃�
-const dbConfig = {
-  host: '139.155.104.10',
-  port: 3306,
-  user: 'ryc',
-  password: 'KiYap3E8X8RLcM6T',
-  database: 'ryc',
-  connectTimeout: 60000,
-  acquireTimeout: 60000,
-  timeout: 60000
-};
-
-async function verifyBirthdayData() {
-  let connection;
-  
-  try {
-    console.log('杩炴帴鏁版嵁搴�...');
-    connection = await mysql.createConnection(dbConfig);
-    
-    // 鏌ヨ鏈�杩戠殑鎶ュ悕璁板綍鍙婂叾鍏宠仈鐨勭敤鎴蜂俊鎭�
-    const query = `
-      SELECT 
-        ap.id as registration_id,
-        ap.project_name,
-        ap.description as registration_description,
-        p.name as player_name,
-        p.phone as player_phone,
-        p.gender,
-        p.education,
-        p.introduction,
-        u.id as user_id,
-        u.name as user_name,
-        u.phone as user_phone,
-        u.birthday,
-        ap.create_time
-      FROM t_activity_player ap
-      LEFT JOIN t_player p ON ap.player_id = p.id
-      LEFT JOIN t_user u ON p.user_id = u.id
-      WHERE ap.id IN (25, 26)
-      ORDER BY ap.create_time DESC
-      LIMIT 5
-    `;
-    
-    console.log('鏌ヨ鏈�杩戠殑鎶ュ悕璁板綍...');
-    const [rows] = await connection.execute(query);
-    
-    if (rows.length > 0) {
-      console.log('\n鉁� 鎵惧埌鎶ュ悕璁板綍:');
-      rows.forEach((row, index) => {
-        console.log(`\n--- 璁板綍 ${index + 1} ---`);
-        console.log(`鎶ュ悕ID: ${row.registration_id}`);
-        console.log(`閫夋墜濮撳悕: ${row.player_name}`);
-        console.log(`閫夋墜鐢佃瘽: ${row.player_phone}`);
-        console.log(`鎬у埆: ${row.gender === 0 ? '鐢�' : '濂�'}`);
-        console.log(`瀛﹀巻: ${row.education}`);
-        console.log(`椤圭洰鍚嶇О: ${row.project_name}`);
-        console.log(`鎶ュ悕鎻忚堪: ${row.registration_description}`);
-        console.log(`鐢ㄦ埛ID: ${row.user_id}`);
-        console.log(`鐢ㄦ埛濮撳悕: ${row.user_name}`);
-        console.log(`鐢ㄦ埛鐢佃瘽: ${row.user_phone}`);
-        console.log(`鐢熸棩: ${row.birthday}`);
-        console.log(`鎶ュ悕鏃堕棿: ${row.create_time}`);
-        
-        if (row.birthday) {
-          console.log('鉁� 鐢熸棩瀛楁宸叉纭繚瀛樺埌鐢ㄦ埛琛�');
-        } else {
-          console.log('鉂� 鐢熸棩瀛楁鏈繚瀛樺埌鐢ㄦ埛琛�');
-        }
-      });
-    } else {
-      console.log('鉂� 鏈壘鍒版姤鍚嶈褰�');
-    }
-    
-  } catch (error) {
-    console.error('鉂� 鏁版嵁搴撴煡璇㈠け璐�:', error.message);
-  } finally {
-    if (connection) {
-      await connection.end();
-      console.log('\n鏁版嵁搴撹繛鎺ュ凡鍏抽棴');
-    }
-  }
-}
-
-verifyBirthdayData();
\ No newline at end of file
diff --git a/web/src/api/activity.js b/web/src/api/activity.js
index 9039862..6ea223a 100644
--- a/web/src/api/activity.js
+++ b/web/src/api/activity.js
@@ -1,6 +1,6 @@
 // 姣旇禌绠$悊 API
 
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql';
+import { API_CONFIG, graphqlRequest } from '@/config/api';
 
 // GraphQL 鏌ヨ璇彞
 const GET_ACTIVITIES_QUERY = `
@@ -61,6 +61,7 @@
         playerMax
         state
         stateName
+        sortOrder
         ratingScheme {
           id
           name
@@ -85,6 +86,7 @@
       name
       state
       stateName
+      sortOrder
       parent {
         id
         name
@@ -118,95 +120,46 @@
 
 // API 鍑芥暟
 export const getActivities = async (page = 0, size = 10, name = '') => {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query: GET_ACTIVITIES_QUERY,
-      variables: { page, size, name }
-    })
-  });
-
-  const result = await response.json();
-  if (result.errors) {
-    throw new Error(result.errors[0].message);
+  try {
+    const data = await graphqlRequest(GET_ACTIVITIES_QUERY, { page, size, name });
+    return data.activities;
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇姣旇禌鍒楄〃澶辫触');
   }
-  return result.data.activities;
 };
 
 export const getActivity = async (id) => {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query: GET_ACTIVITY_QUERY,
-      variables: { id }
-    })
-  });
-
-  const result = await response.json();
-  if (result.errors) {
-    throw new Error(result.errors[0].message);
+  try {
+    const data = await graphqlRequest(GET_ACTIVITY_QUERY, { id });
+    return data.activity;
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇姣旇禌璇︽儏澶辫触');
   }
-  return result.data.activity;
 };
 
 export const getAllActivities = async () => {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query: GET_ALL_ACTIVITIES_QUERY
-    })
-  });
-
-  const result = await response.json();
-  if (result.errors) {
-    throw new Error(result.errors[0].message);
+  try {
+    const data = await graphqlRequest(GET_ALL_ACTIVITIES_QUERY);
+    return data.allActivities;
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇鎵�鏈夋瘮璧涘け璐�');
   }
-  return result.data.allActivities;
 };
 
 export const saveActivity = async (activityData) => {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query: SAVE_ACTIVITY_MUTATION,
-      variables: { input: activityData }
-    })
-  });
-
-  const result = await response.json();
-  if (result.errors) {
-    throw new Error(result.errors[0].message);
+  try {
+    const data = await graphqlRequest(SAVE_ACTIVITY_MUTATION, { input: activityData });
+    return data.saveActivity;
+  } catch (error) {
+    throw new Error(error.message || '淇濆瓨姣旇禌澶辫触');
   }
-  return result.data.saveActivity;
 };
 
 export const deleteActivity = async (id) => {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query: DELETE_ACTIVITY_MUTATION,
-      variables: { id }
-    })
-  });
-
-  const result = await response.json();
-  if (result.errors) {
-    throw new Error(result.errors[0].message);
+  try {
+    const data = await graphqlRequest(DELETE_ACTIVITY_MUTATION, { id });
+    return data.deleteActivity;
+  } catch (error) {
+    throw new Error(error.message || '鍒犻櫎姣旇禌澶辫触');
   }
-  return result.data.deleteActivity;
 };
\ No newline at end of file
diff --git a/web/src/api/activityPlayer.js b/web/src/api/activityPlayer.js
index 6c0ef11..0f6bdb4 100644
--- a/web/src/api/activityPlayer.js
+++ b/web/src/api/activityPlayer.js
@@ -8,25 +8,145 @@
   createMockResponse 
 } from './mockData.js'
 
-const GRAPHQL_ENDPOINT = '/api/graphql'
+import { API_CONFIG, graphqlRequest } from '@/config/api'
 
 // 妯℃嫙鏁版嵁寮�鍏� - 璁剧疆涓簍rue鏃朵娇鐢ㄦā鎷熸暟鎹�
-// 宸插垏鎹㈠埌鐪熷疄鏁版嵁妯″紡
 const USE_MOCK_DATA = false
 
-async function graphqlRequest(query, variables = {}) {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ query, variables })
-  })
-  if (!response.ok) {
-    const text = await response.text()
-    throw new Error(`HTTP ${response.status}: ${text}`)
+// GraphQL鏌ヨ璇彞
+const GET_ACTIVITY_PLAYERS_QUERY = `
+  query GetActivityPlayers($activityId: ID!, $page: Int!, $size: Int!, $name: String) {
+    activityPlayers(activityId: $activityId, page: $page, size: $size, name: $name) {
+      content {
+        id
+        playerId
+        activityId
+        signupTime
+        state
+        stateName
+        player {
+          id
+          name
+          phone
+          regionId
+          region {
+            id
+            name
+          }
+        }
+      }
+      totalElements
+      page
+      size
+    }
   }
-  const result = await response.json()
-  if (result.errors) throw new Error(result.errors.map(e => e.message).join('\n'))
-  return result.data
+`
+
+const GET_ACTIVITY_PLAYER_QUERY = `
+  query GetActivityPlayer($id: ID!) {
+    activityPlayer(id: $id) {
+      id
+      playerId
+      activityId
+      signupTime
+      state
+      stateName
+      player {
+        id
+        name
+        phone
+        regionId
+        region {
+          id
+          name
+        }
+      }
+      activity {
+        id
+        name
+        description
+      }
+      attachments {
+        id
+        fileName
+        fileUrl
+        fileSize
+        uploadTime
+      }
+    }
+  }
+`
+
+const SAVE_ACTIVITY_PLAYER_MUTATION = `
+  mutation SaveActivityPlayer($input: ActivityPlayerInput!) {
+    saveActivityPlayer(input: $input) {
+      id
+      playerId
+      activityId
+      signupTime
+      state
+      stateName
+    }
+  }
+`
+
+const DELETE_ACTIVITY_PLAYER_MUTATION = `
+  mutation DeleteActivityPlayer($id: ID!) {
+    deleteActivityPlayer(id: $id)
+  }
+`
+
+// API鍑芥暟
+export const getActivityPlayers = async (activityId, page = 0, size = 10, name = '') => {
+  if (USE_MOCK_DATA) {
+    return mockActivityPlayers
+  }
+  
+  try {
+    const data = await graphqlRequest(GET_ACTIVITY_PLAYERS_QUERY, { activityId, page, size, name })
+    return data.activityPlayers
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇姣旇禌鎶ュ悕鍒楄〃澶辫触')
+  }
+}
+
+export const getActivityPlayer = async (id) => {
+  if (USE_MOCK_DATA) {
+    return mockActivityPlayerDetail
+  }
+  
+  try {
+    const data = await graphqlRequest(GET_ACTIVITY_PLAYER_QUERY, { id })
+    return data.activityPlayer
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇姣旇禌鎶ュ悕璇︽儏澶辫触')
+  }
+}
+
+export const saveActivityPlayer = async (activityPlayerData) => {
+  if (USE_MOCK_DATA) {
+    return { ...activityPlayerData, id: Date.now().toString() }
+  }
+  
+  try {
+    const data = await graphqlRequest(SAVE_ACTIVITY_PLAYER_MUTATION, { input: activityPlayerData })
+    return data.saveActivityPlayer
+  } catch (error) {
+    throw new Error(error.message || '淇濆瓨姣旇禌鎶ュ悕澶辫触')
+  }
+}
+
+export const deleteActivityPlayer = async (id) => {
+  if (USE_MOCK_DATA) {
+    return true
+  }
+  
+  try {
+    const data = await graphqlRequest(DELETE_ACTIVITY_PLAYER_MUTATION, { id })
+    return data.deleteActivityPlayer
+  } catch (error) {
+    throw new Error(error.message || '鍒犻櫎姣旇禌鎶ュ悕澶辫触')
+  }
 }
 
 const GET_ACTIVITY_PLAYER_DETAIL = `
@@ -46,11 +166,15 @@
         fullPath
       }
       activityName
+      projectName
       description
+      feedback
+      state
       submissionFiles {
         id
         name
         url
+        thumbUrl
         fileExt
         fileSize
         mediaType
@@ -193,4 +317,44 @@
     }))
   }
   return graphqlRequest(GET_CURRENT_JUDGE_INFO)
+}
+
+// 瀹℃牳鐩稿叧mutations
+const APPROVE_ACTIVITY_PLAYER = `
+  mutation ApproveActivityPlayer($activityPlayerId: ID!, $feedback: String) {
+    approveActivityPlayer(activityPlayerId: $activityPlayerId, feedback: $feedback)
+  }
+`
+
+const REJECT_ACTIVITY_PLAYER = `
+  mutation RejectActivityPlayer($activityPlayerId: ID!, $feedback: String!) {
+    rejectActivityPlayer(activityPlayerId: $activityPlayerId, feedback: $feedback)
+  }
+`
+
+const UPDATE_PLAYER_FEEDBACK = `
+  mutation UpdatePlayerFeedback($activityPlayerId: ID!, $feedback: String!) {
+    updatePlayerFeedback(activityPlayerId: $activityPlayerId, feedback: $feedback)
+  }
+`
+
+/**
+ * 瀹℃牳閫氳繃
+ */
+export function approveActivityPlayer(activityPlayerId, feedback = '') {
+  return graphqlRequest(APPROVE_ACTIVITY_PLAYER, { activityPlayerId, feedback })
+}
+
+/**
+ * 瀹℃牳椹冲洖
+ */
+export function rejectActivityPlayer(activityPlayerId, feedback) {
+  return graphqlRequest(REJECT_ACTIVITY_PLAYER, { activityPlayerId, feedback })
+}
+
+/**
+ * 鏇存柊瀹℃牳鎰忚
+ */
+export function updatePlayerFeedback(activityPlayerId, feedback) {
+  return graphqlRequest(UPDATE_PLAYER_FEEDBACK, { activityPlayerId, feedback })
 }
\ No newline at end of file
diff --git a/web/src/api/carousel.js b/web/src/api/carousel.js
index 773f117..802b3f6 100644
--- a/web/src/api/carousel.js
+++ b/web/src/api/carousel.js
@@ -1,30 +1,4 @@
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql'
-
-// GraphQL璇锋眰鍑芥暟
-async function graphqlRequest(query, variables = {}) {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query,
-      variables,
-    }),
-  })
-
-  if (!response.ok) {
-    throw new Error(`HTTP error! status: ${response.status}`)
-  }
-
-  const result = await response.json()
-  
-  if (result.errors) {
-    throw new Error(result.errors[0].message)
-  }
-
-  return result.data
-}
+import { graphqlRequest } from '../config/api.ts'
 
 // GraphQL 鏌ヨ鍜屽彉鏇�
 const GET_CAROUSELS = `
diff --git a/web/src/api/config.js b/web/src/api/config.js
index 3197b69..09f9fa0 100644
--- a/web/src/api/config.js
+++ b/web/src/api/config.js
@@ -1,21 +1,49 @@
 // 搴旂敤閰嶇疆 API
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql';
+import { API_CONFIG, graphqlRequest } from '@/config/api';
 
 const GET_APP_CONFIG = `
-  query AppConfig {
+  query GetAppConfig {
     appConfig {
-      mediaBaseUrl
+      id
+      name
+      value
+      description
+      type
+      createTime
+      updateTime
     }
   }
 `;
 
-export const fetchAppConfig = async () => {
-  const resp = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ query: GET_APP_CONFIG })
-  });
-  const result = await resp.json();
-  if (result.errors) throw new Error(result.errors[0].message);
-  return result.data.appConfig;
+const SAVE_APP_CONFIG = `
+  mutation SaveAppConfig($input: AppConfigInput!) {
+    saveAppConfig(input: $input) {
+      id
+      name
+      value
+      description
+      type
+      createTime
+      updateTime
+    }
+  }
+`;
+
+// API 鍑芥暟
+export const getAppConfig = async () => {
+  try {
+    const data = await graphqlRequest(GET_APP_CONFIG);
+    return data.appConfig || [];
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇搴旂敤閰嶇疆澶辫触');
+  }
+};
+
+export const saveAppConfig = async (configData) => {
+  try {
+    const data = await graphqlRequest(SAVE_APP_CONFIG, { input: configData });
+    return data.saveAppConfig;
+  } catch (error) {
+    throw new Error(error.message || '淇濆瓨搴旂敤閰嶇疆澶辫触');
+  }
 };
\ No newline at end of file
diff --git a/web/src/api/dashboard.js b/web/src/api/dashboard.js
index 90aa3c9..ef2e5d1 100644
--- a/web/src/api/dashboard.js
+++ b/web/src/api/dashboard.js
@@ -1,5 +1,4 @@
-// Dashboard API
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql';
+import { graphqlRequest } from '../config/api'
 
 // GraphQL 鏌ヨ璇彞
 const GET_DASHBOARD_STATS_QUERY = `
@@ -15,19 +14,6 @@
 
 // API 鍑芥暟
 export const getDashboardStats = async () => {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query: GET_DASHBOARD_STATS_QUERY
-    })
-  });
-
-  const result = await response.json();
-  if (result.errors) {
-    throw new Error(result.errors[0].message);
-  }
-  return result.data.dashboardStats;
+  const data = await graphqlRequest(GET_DASHBOARD_STATS_QUERY);
+  return data.dashboardStats;
 };
\ No newline at end of file
diff --git a/web/src/api/employee.ts b/web/src/api/employee.ts
index dd55bc7..75c9dac 100644
--- a/web/src/api/employee.ts
+++ b/web/src/api/employee.ts
@@ -1,5 +1,7 @@
 // 鍛樺伐绠$悊API
 
+import { API_CONFIG, graphqlRequest } from '@/config/api'
+
 // 鍛樺伐鐩稿叧鐨� GraphQL 鏌ヨ鍜屽彉鏇�
 export const EMPLOYEE_QUERIES = {
   // 鑾峰彇鎵�鏈夊憳宸�
@@ -94,92 +96,41 @@
 
 // API 鍑芥暟
 export const employeeApi = {
-  // 鑾峰彇鎵�鏈夊憳宸�
+  // 鑾峰彇鍛樺伐鍒楄〃
   async getEmployees(): Promise<Employee[]> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: EMPLOYEE_QUERIES.GET_EMPLOYEES
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.employees || []
+      const data = await graphqlRequest(EMPLOYEE_QUERIES.GET_EMPLOYEES)
+      return data?.employees || []
     } catch (error: any) {
       throw new Error(error.message || '鑾峰彇鍛樺伐鍒楄〃澶辫触')
     }
   },
 
-  // 鏍规嵁鍚嶇О鎼滅储鍛樺伐
-  async searchEmployees(name?: string): Promise<Employee[]> {
+  // 鎼滅储鍛樺伐
+  async searchEmployees(keyword: string): Promise<Employee[]> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: EMPLOYEE_QUERIES.SEARCH_EMPLOYEES,
-          variables: { name }
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.employeesByName || []
+      const data = await graphqlRequest(EMPLOYEE_QUERIES.SEARCH_EMPLOYEES, { keyword })
+      return data?.searchEmployees || []
     } catch (error: any) {
       throw new Error(error.message || '鎼滅储鍛樺伐澶辫触')
     }
   },
 
-  // 鑾峰彇鍛樺伐璇︽儏
+  // 鏍规嵁ID鑾峰彇鍛樺伐璇︽儏
   async getEmployee(id: string): Promise<Employee | null> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: EMPLOYEE_QUERIES.GET_EMPLOYEE,
-          variables: { id }
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.employee || null
+      const data = await graphqlRequest(EMPLOYEE_QUERIES.GET_EMPLOYEE, { id })
+      return data?.employee || null
     } catch (error: any) {
       throw new Error(error.message || '鑾峰彇鍛樺伐璇︽儏澶辫触')
     }
   },
 
-  // 淇濆瓨鍛樺伐
-  async saveEmployee(input: EmployeeInput): Promise<Employee> {
+  // 淇濆瓨鍛樺伐锛堟柊澧炴垨鏇存柊锛�
+  async saveEmployee(employee: EmployeeInput): Promise<Employee> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: EMPLOYEE_MUTATIONS.SAVE_EMPLOYEE,
-          variables: { input }
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.saveEmployee
+      const data = await graphqlRequest(EMPLOYEE_MUTATIONS.SAVE_EMPLOYEE, { input: employee })
+      return data?.saveEmployee
     } catch (error: any) {
       throw new Error(error.message || '淇濆瓨鍛樺伐澶辫触')
     }
@@ -188,21 +139,8 @@
   // 鍒犻櫎鍛樺伐
   async deleteEmployee(id: string): Promise<boolean> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: EMPLOYEE_MUTATIONS.DELETE_EMPLOYEE,
-          variables: { id }
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.deleteEmployee || false
+      const data = await graphqlRequest(EMPLOYEE_MUTATIONS.DELETE_EMPLOYEE, { id })
+      return data?.deleteEmployee || false
     } catch (error: any) {
       throw new Error(error.message || '鍒犻櫎鍛樺伐澶辫触')
     }
diff --git a/web/src/api/graphql.ts b/web/src/api/graphql.ts
index 7dd2e56..865cdd5 100644
--- a/web/src/api/graphql.ts
+++ b/web/src/api/graphql.ts
@@ -2,7 +2,7 @@
 
 // GraphQL 瀹㈡埛绔厤缃�
 export const graphqlClient = new Client({
-  url: 'http://localhost:8080/api/graphql',
+  url: '/api/graphql',
   exchanges: [cacheExchange, fetchExchange],
 })
 
diff --git a/web/src/api/judge.js b/web/src/api/judge.js
index de2a207..21ff463 100644
--- a/web/src/api/judge.js
+++ b/web/src/api/judge.js
@@ -1,6 +1,6 @@
 // 璇勫绠$悊 API
 
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql';
+const GRAPHQL_ENDPOINT = '/api/graphql';
 
 // GraphQL 鏌ヨ璇彞
 const GET_ALL_JUDGES_QUERY = `
diff --git a/web/src/api/judge.ts b/web/src/api/judge.ts
index 9319200..73fa1d9 100644
--- a/web/src/api/judge.ts
+++ b/web/src/api/judge.ts
@@ -1,33 +1,9 @@
 import { JUDGE_QUERIES, JUDGE_MUTATIONS } from './graphql'
 import type { Judge, JudgeInput, CosCredentials, Tag, Media } from './graphql'
 
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql'
+import { API_CONFIG, graphqlRequest } from '@/config/api'
 
-// GraphQL璇锋眰鍑芥暟
-async function graphqlRequest(query: string, variables: any = {}) {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query,
-      variables,
-    }),
-  })
-
-  if (!response.ok) {
-    throw new Error(`HTTP error! status: ${response.status}`)
-  }
-
-  const result = await response.json()
-  
-  if (result.errors) {
-    throw new Error(result.errors[0]?.message || 'GraphQL error')
-  }
-
-  return result.data
-}
+// 浣跨敤缁熶竴鐨凣raphQL璇锋眰鍑芥暟
 
 export class JudgeApi {
   // 鑾峰彇鎵�鏈夎瘎濮�
diff --git a/web/src/api/media.js b/web/src/api/media.js
index 1934b37..6c7afdc 100644
--- a/web/src/api/media.js
+++ b/web/src/api/media.js
@@ -1,5 +1,7 @@
 // 濯掍綋鏌ヨ API
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql';
+import { graphqlRequest, API_CONFIG } from '../config/api.ts';
+
+const GRAPHQL_ENDPOINT = API_CONFIG.GRAPHQL_ENDPOINT;
 
 const MEDIAS_BY_TARGET_QUERY = `
   query MediasByTarget($targetType: Int!, $targetId: ID!) {
@@ -36,9 +38,17 @@
 `;
 
 export const getMediasByTarget = async (targetType, targetId) => {
+  // 鑾峰彇JWT token
+  const { getToken } = await import('@/utils/auth');
+  const token = getToken();
+  const headers = { 'Content-Type': 'application/json' };
+  if (token) {
+    headers['Authorization'] = `Bearer ${token}`;
+  }
+
   const res = await fetch(GRAPHQL_ENDPOINT, {
     method: 'POST',
-    headers: { 'Content-Type': 'application/json' },
+    headers: headers,
     body: JSON.stringify({
       query: MEDIAS_BY_TARGET_QUERY,
       variables: { targetType, targetId }
@@ -52,9 +62,17 @@
 };
 
 export const saveMedia = async (input) => {
+  // 鑾峰彇JWT token
+  const { getToken } = await import('@/utils/auth');
+  const token = getToken();
+  const headers = { 'Content-Type': 'application/json' };
+  if (token) {
+    headers['Authorization'] = `Bearer ${token}`;
+  }
+
   const res = await fetch(GRAPHQL_ENDPOINT, {
     method: 'POST',
-    headers: { 'Content-Type': 'application/json' },
+    headers: headers,
     body: JSON.stringify({
       query: SAVE_MEDIA_MUTATION,
       variables: { input }
@@ -72,9 +90,17 @@
   console.log('瑕佸垹闄ょ殑濯掍綋ID:', id);
   console.log('GraphQL鏌ヨ:', DELETE_MEDIA_MUTATION);
   
+  // 鑾峰彇JWT token
+  const { getToken } = await import('@/utils/auth');
+  const token = getToken();
+  const headers = { 'Content-Type': 'application/json' };
+  if (token) {
+    headers['Authorization'] = `Bearer ${token}`;
+  }
+  
   const res = await fetch(GRAPHQL_ENDPOINT, {
     method: 'POST',
-    headers: { 'Content-Type': 'application/json' },
+    headers: headers,
     body: JSON.stringify({
       query: DELETE_MEDIA_MUTATION,
       variables: { id: id.toString() }
@@ -99,8 +125,17 @@
   const formData = new FormData();
   formData.append('file', file);
   
+  // 鑾峰彇JWT token
+  const { getToken } = await import('@/utils/auth');
+  const token = getToken();
+  const headers = {};
+  if (token) {
+    headers['Authorization'] = `Bearer ${token}`;
+  }
+  
   const response = await fetch('http://localhost:8080/api/upload/image', {
     method: 'POST',
+    headers: headers,
     body: formData
   });
   
@@ -151,4 +186,35 @@
     console.error('瑙嗛澶勭悊澶辫触:', error);
     throw new Error(`瑙嗛澶勭悊澶辫触: ${error.message}`);
   }
+};
+
+// 鏂扮増淇濆瓨濯掍綋锛圫aveMediaV2锛夛細浣跨敤瀛楃涓� targetType 涓� MediaSaveInput
+// 娉ㄦ剰锛氱洰鍓嶅悗绔粎鏀寔 targetType: "player"锛堝鍛樺ご鍍� -> 6锛変笌 "activity_player"锛堟姤鍚嶈祫鏂� -> 5锛夈��
+// 瀛楁鍛藉悕涓庢棫鐗堜笉鍚岋細fileName 鏇夸唬 name锛涘彲閫� thumbPath锛沵ediaType: 1鍥剧墖銆�2瑙嗛銆�3闊抽銆�4鏂囨。銆�
+const SAVE_MEDIA_V2_MUTATION = `
+  mutation SaveMediaV2($input: MediaSaveInput!) {
+    saveMediaV2(input: $input) {
+      success
+      message
+      mediaId
+    }
+  }
+`;
+
+// 缁熶竴鐨� V2 淇濆瓨鎺ュ彛锛堣繑鍥� { success, message, mediaId }锛夛紝绀轰緥锛�
+// await saveMediaV2({ targetType: 'player', targetId: 123, path: 'avatar/xxx.jpg', fileName: 'avatar.jpg', fileExt: 'jpg', fileSize: 2048, mediaType: 1 })
+export const saveMediaV2 = async (input) => {
+  const res = await fetch(GRAPHQL_ENDPOINT, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({
+      query: SAVE_MEDIA_V2_MUTATION,
+      variables: { input }
+    })
+  });
+  const result = await res.json();
+  if (result.errors) {
+    throw new Error(result.errors[0].message);
+  }
+  return result.data.saveMediaV2;
 };
\ No newline at end of file
diff --git a/web/src/api/player.js b/web/src/api/player.js
index a1ada54..34cec08 100644
--- a/web/src/api/player.js
+++ b/web/src/api/player.js
@@ -1,31 +1,118 @@
-const GRAPHQL_ENDPOINT = '/api/graphql'
+import { API_CONFIG, graphqlRequest } from '@/config/api'
 
-async function graphqlRequest(query, variables = {}) {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: { 'Content-Type': 'application/json' },
-    body: JSON.stringify({ query, variables })
-  })
-  if (!response.ok) {
-    const text = await response.text()
-    throw new Error(`HTTP ${response.status}: ${text}`)
+// 浣跨敤缁熶竴鐨刧raphqlRequest鍑芥暟
+
+// GraphQL鏌ヨ璇彞
+const GET_PLAYERS_QUERY = `
+  query GetPlayers($page: Int!, $size: Int!, $name: String) {
+    players(page: $page, size: $size, name: $name) {
+      content {
+        id
+        name
+        phone
+        regionId
+        region {
+          id
+          name
+        }
+        createTime
+        updateTime
+      }
+      totalElements
+      page
+      size
+    }
   }
-  const result = await response.json()
-  if (result.errors) throw new Error(result.errors.map(e => e.message).join('\\n'))
-  return result.data
+`
+
+const GET_PLAYER_QUERY = `
+  query GetPlayer($id: ID!) {
+    player(id: $id) {
+      id
+      name
+      phone
+      regionId
+      region {
+        id
+        name
+      }
+      createTime
+      updateTime
+    }
+  }
+`
+
+const SAVE_PLAYER_MUTATION = `
+  mutation SavePlayer($input: PlayerInput!) {
+    savePlayer(input: $input) {
+      id
+      name
+      phone
+      regionId
+      region {
+        id
+        name
+      }
+      createTime
+      updateTime
+    }
+  }
+`
+
+const DELETE_PLAYER_MUTATION = `
+  mutation DeletePlayer($id: ID!) {
+    deletePlayer(id: $id)
+  }
+`
+
+// API鍑芥暟
+export const getPlayers = async (page = 0, size = 10, name = '') => {
+  try {
+    const data = await graphqlRequest(GET_PLAYERS_QUERY, { page, size, name })
+    return data.players
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇瀛﹀憳鍒楄〃澶辫触')
+  }
+}
+
+export const getPlayer = async (id) => {
+  try {
+    const data = await graphqlRequest(GET_PLAYER_QUERY, { id })
+    return data.player
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇瀛﹀憳璇︽儏澶辫触')
+  }
+}
+
+export const savePlayer = async (playerData) => {
+  try {
+    const data = await graphqlRequest(SAVE_PLAYER_MUTATION, { input: playerData })
+    return data.savePlayer
+  } catch (error) {
+    throw new Error(error.message || '淇濆瓨瀛﹀憳澶辫触')
+  }
+}
+
+export const deletePlayer = async (id) => {
+  try {
+    const data = await graphqlRequest(DELETE_PLAYER_MUTATION, { id })
+    return data.deletePlayer
+  } catch (error) {
+    throw new Error(error.message || '鍒犻櫎瀛﹀憳澶辫触')
+  }
 }
 
 const GET_APPLICATIONS = `
-  query GetApplications($name: String, $activityId: ID, $page: Int, $size: Int) {
-    activityPlayerApplications(name: $name, activityId: $activityId, page: $page, size: $size) {
+  query GetApplications($name: String, $activityId: ID, $state: Int, $page: Int, $size: Int) {
+    activityPlayerApplications(name: $name, activityId: $activityId, state: $state, page: $page, size: $size) {
       id playerName activityName phone applyTime state
     }
   }
 `
 
 export const PlayerApi = {
-  getApplications: async (name, activityId, page, size) => {
-    const data = await graphqlRequest(GET_APPLICATIONS, { name, activityId, page, size })
+  getApplications: async (name, activityId, state, page, size) => {
+    const data = await graphqlRequest(GET_APPLICATIONS, { name, activityId, state, page, size })
     return data.activityPlayerApplications || []
   }
 }
\ No newline at end of file
diff --git a/web/src/api/projectReview.js b/web/src/api/projectReview.js
new file mode 100644
index 0000000..cbfaa9e
--- /dev/null
+++ b/web/src/api/projectReview.js
@@ -0,0 +1,279 @@
+import { graphqlRequest } from '@/config/api'
+
+// GraphQL 鏌ヨ璇彞
+
+// 鑾峰彇杩涜涓殑姣旇禌鍒楄〃锛堝寘鎷樁娈碉級
+const GET_ACTIVE_ACTIVITIES_QUERY = `
+  query GetActiveActivities {
+    allActivities {
+      id
+      pid
+      name
+      state
+      parent {
+        id
+        name
+      }
+    }
+  }
+`
+
+// 鑾峰彇姣旇禌椤圭洰鍒楄〃锛堝垎椤碉級
+const GET_COMPETITION_PROJECTS_QUERY = `
+  query GetCompetitionProjects($activityId: ID, $page: Int, $size: Int) {
+    activityPlayerApplications(activityId: $activityId, page: $page, size: $size) {
+      id
+      playerName
+      activityName
+      projectName
+      phone
+      applyTime
+      state
+    }
+  }
+`
+
+// 鑾峰彇椤圭洰璇︽儏
+export const GET_PROJECT_DETAIL_QUERY = `
+  query GetProjectDetail($id: ID!) {
+    activityPlayerDetail(id: $id) {
+      id
+      playerInfo {
+        id
+        name
+        phone
+        gender
+        education
+        introduction
+        avatarUrl
+        birthday
+      }
+      regionInfo {
+        name
+      }
+      activityName
+      projectName
+      description
+      submissionFiles {
+        id
+        name
+        url
+        thumbUrl
+        fileExt
+        fileSize
+        mediaType
+      }
+      ratingForm {
+        schemeId
+        schemeName
+        totalMaxScore
+        items {
+          id
+          name
+          maxScore
+          orderNo
+        }
+      }
+      state
+      feedback
+    }
+  }
+`
+
+// 鑾峰彇璇勫缁熻淇℃伅
+export const GET_RATING_STATS_QUERY = `
+  query GetRatingStats($activityPlayerId: ID!) {
+    judgeRatingsForPlayer(activityPlayerId: $activityPlayerId) {
+      judgeId
+      judgeName
+      hasRated
+      totalScore
+      ratingTime
+    }
+    averageScoreForPlayer(activityPlayerId: $activityPlayerId)
+  }
+`
+
+// 鑾峰彇褰撳墠璇勫鐨勮瘎鍒嗕俊鎭�
+const GET_CURRENT_JUDGE_RATING_QUERY = `
+  query GetCurrentJudgeRating($activityPlayerId: ID!) {
+    currentJudgeRating(activityPlayerId: $activityPlayerId) {
+      id
+      totalScore
+      comments
+      ratingItems {
+        itemId
+        itemName
+        score
+        maxScore
+      }
+    }
+  }
+`
+
+// 鎻愪氦璇勫垎
+const SUBMIT_RATING_MUTATION = `
+  mutation SubmitRating($input: ActivityPlayerRatingInput!) {
+    saveActivityPlayerRating(input: $input)
+  }
+`
+
+// API 鍑芥暟
+
+/**
+ * 鑾峰彇杩涜涓殑姣旇禌闃舵鍒楄〃锛坰tate=1涓攑id>0鐨勬瘮璧涢樁娈碉級
+ * 鎸夋瘮璧涘垎缁勬帓搴忥紝鏄剧ず鏍煎紡涓�"姣旇禌鍚� + 闃舵鍚�"
+ */
+export const getActiveActivities = async () => {
+  try {
+    const data = await graphqlRequest(GET_ACTIVE_ACTIVITIES_QUERY)
+    
+    // 杩囨护鍑簊tate=1涓攑id>0鐨勬瘮璧涢樁娈�
+    const stages = data.allActivities.filter(activity => 
+      activity.state === 1 && activity.pid > 0
+    )
+    
+    // 鎸夋瘮璧汭D(pid)鍒嗙粍鎺掑簭锛岀‘淇濆悓涓�姣旇禌鐨勪笉鍚岄樁娈垫斁鍦ㄤ竴璧�
+    stages.sort((a, b) => {
+      // 棣栧厛鎸夋瘮璧汭D鎺掑簭
+      if (a.pid !== b.pid) {
+        return a.pid - b.pid
+      }
+      // 鍚屼竴姣旇禌鍐呮寜闃舵ID鎺掑簭
+      return a.id - b.id
+    })
+    
+    return stages
+  } catch (error) {
+    console.error('鑾峰彇娲诲姩鍒楄〃澶辫触:', error)
+    throw new Error(error.message || '鑾峰彇娲诲姩鍒楄〃澶辫触')
+  }
+}
+
+/**
+ * 鑾峰彇姣旇禌椤圭洰鍒楄〃
+ */
+export const getCompetitionProjects = async (activityId, page = 0, size = 10) => {
+  try {
+    const data = await graphqlRequest(GET_COMPETITION_PROJECTS_QUERY, {
+      activityId,
+      page,
+      size
+    })
+    
+    const projects = data.activityPlayerApplications || []
+    
+    // 涓烘瘡涓」鐩幏鍙栬瘎鍒嗙粺璁�
+    const enrichedProjects = await Promise.all(
+      projects.map(async (project) => {
+        try {
+          const stats = await getRatingStats(project.id)
+          return {
+            id: project.id,
+            playerName: project.playerName,
+            activityName: project.activityName,
+            phone: project.phone,
+            applyTime: project.applyTime,
+            state: project.state,
+            projectName: project.projectName || project.playerName + '鐨勯」鐩�', // 浣跨敤鐪熷疄椤圭洰鍚嶇О锛屽鏋滀负绌哄垯浣跨敤榛樿鍚嶇О
+            ratingCount: stats.ratingCount,
+            averageScore: stats.averageScore
+          }
+        } catch (error) {
+          console.warn(`Failed to get rating stats for project ${project.id}:`, error)
+          return {
+            id: project.id,
+            playerName: project.playerName,
+            activityName: project.activityName,
+            phone: project.phone,
+            applyTime: project.applyTime,
+            state: project.state,
+            projectName: project.projectName || project.playerName + '鐨勯」鐩�', // 浣跨敤鐪熷疄椤圭洰鍚嶇О锛屽鏋滀负绌哄垯浣跨敤榛樿鍚嶇О
+            ratingCount: 0,
+            averageScore: 0
+          }
+        }
+      })
+    )
+    
+    // 妯℃嫙鍒嗛〉淇℃伅锛屽洜涓篈PI杩斿洖鐨勬槸鏁扮粍鑰屼笉鏄垎椤靛璞�
+    const totalElements = projects.length
+    const totalPages = Math.ceil(totalElements / size)
+    
+    return {
+      content: enrichedProjects,
+      totalElements,
+      totalPages,
+      number: page,
+      size
+    }
+  } catch (error) {
+    console.error('Error fetching competition projects:', error)
+    throw error
+  }
+}
+
+/**
+ * 鑾峰彇椤圭洰璇︽儏
+ */
+export const getProjectDetail = async (id) => {
+  try {
+    const data = await graphqlRequest(GET_PROJECT_DETAIL_QUERY, { id })
+    return data.activityPlayerDetail
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇椤圭洰璇︽儏澶辫触')
+  }
+}
+
+/**
+ * 鑾峰彇璇勫缁熻淇℃伅
+ */
+export const getRatingStats = async (activityPlayerId) => {
+  try {
+    const data = await graphqlRequest(GET_RATING_STATS_QUERY, { activityPlayerId })
+    
+    const ratings = data.judgeRatingsForPlayer || []
+    const averageScore = data.averageScoreForPlayer || 0
+    
+    // 鍙绠楀凡璇勫垎鐨勮瘎濮�
+    const ratedJudges = ratings.filter(rating => rating.hasRated)
+    
+    return {
+      ratingCount: ratedJudges.length,
+      averageScore: averageScore,
+      ratings: ratings
+    }
+  } catch (error) {
+    console.error('Error fetching rating stats:', error)
+    // 杩斿洖榛樿鍊艰�屼笉鏄姏鍑洪敊璇紝閬垮厤褰卞搷涓昏鍔熻兘
+    return {
+      ratingCount: 0,
+      averageScore: 0,
+      ratings: []
+    }
+  }
+}
+
+/**
+ * 鑾峰彇褰撳墠璇勫鐨勮瘎鍒嗕俊鎭�
+ */
+export const getCurrentJudgeRating = async (activityPlayerId) => {
+  try {
+    const data = await graphqlRequest(GET_CURRENT_JUDGE_RATING_QUERY, { activityPlayerId })
+    return data.currentJudgeRating
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇褰撳墠璇勫璇勫垎澶辫触')
+  }
+}
+
+/**
+ * 鎻愪氦璇勫垎
+ */
+export const submitRating = async (ratingInput) => {
+  try {
+    const data = await graphqlRequest(SUBMIT_RATING_MUTATION, { input: ratingInput })
+    return data.saveActivityPlayerRating
+  } catch (error) {
+    throw new Error(error.message || '鎻愪氦璇勫垎澶辫触')
+  }
+}
\ No newline at end of file
diff --git a/web/src/api/promotion.js b/web/src/api/promotion.js
new file mode 100644
index 0000000..6094476
--- /dev/null
+++ b/web/src/api/promotion.js
@@ -0,0 +1,136 @@
+import { API_CONFIG, graphqlRequest } from '@/config/api'
+
+// GraphQL 鏌ヨ锛氳幏鍙栨瘮璧涙檵绾у垪琛�
+const GET_PROMOTION_COMPETITIONS = `
+  query GetPromotionCompetitions($name: String, $page: Int, $size: Int) {
+    promotionCompetitions(name: $name, page: $page, size: $size) {
+      id
+      competitionName
+      stageName
+      maxParticipants
+      currentCount
+      status
+      startTime
+      endTime
+      sortOrder
+      state
+    }
+  }
+`
+
+// GraphQL 鏌ヨ锛氳幏鍙栨瘮璧涘弬璧涗汉鍛�
+const GET_COMPETITION_PARTICIPANTS = `
+  query GetCompetitionParticipants($competitionId: ID!, $page: Int, $size: Int) {
+    competitionParticipants(competitionId: $competitionId, page: $page, size: $size) {
+      id
+      playerName
+      projectName
+      phone
+      averageScore
+      ratingCount
+      applyTime
+      state
+    }
+  }
+`
+
+// GraphQL 鏌ヨ锛氳幏鍙栧彲鏅嬬骇鍙傝禌鑰�
+const GET_PROMOTABLE_PARTICIPANTS = `
+  query GetPromotableParticipants($currentStageId: ID!) {
+    promotableParticipants(currentStageId: $currentStageId) {
+      participants {
+        id
+        playerId
+        playerName
+        projectName
+        phone
+        averageScore
+        ratingCount
+        applyTime
+        state
+      }
+      selectableCount
+      totalCount
+      previousStageName
+      currentStageName
+    }
+  }
+`
+
+// GraphQL 鍙樻洿锛氭檵绾у弬璧涜��
+const PROMOTE_PARTICIPANTS = `
+  mutation PromoteParticipants($input: PromotionInput!) {
+    promoteParticipants(input: $input) {
+      success
+      message
+      promotedCount
+    }
+  }
+`
+
+// 姣旇禌鏅嬬骇 API
+export const PromotionApi = {
+  // 鑾峰彇姣旇禌鏅嬬骇鍒楄〃
+  async getPromotionCompetitions(params = {}) {
+    try {
+      const variables = {
+        name: params.name || null,
+        page: params.page || 1,
+        size: params.size || 10
+      }
+      const data = await graphqlRequest(GET_PROMOTION_COMPETITIONS, variables)
+      return data.promotionCompetitions || []
+    } catch (error) {
+      console.error('鑾峰彇姣旇禌鏅嬬骇鍒楄〃澶辫触:', error)
+      throw error
+    }
+  },
+
+  // 鑾峰彇姣旇禌鍙傝禌浜哄憳
+  async getCompetitionParticipants(competitionId, params = {}) {
+    try {
+      const variables = {
+        competitionId,
+        page: params.page || 1,
+        size: params.size || 10
+      }
+      const data = await graphqlRequest(GET_COMPETITION_PARTICIPANTS, variables)
+      return data.competitionParticipants || []
+    } catch (error) {
+      console.error('鑾峰彇姣旇禌鍙傝禌浜哄憳澶辫触:', error)
+      throw error
+    }
+  },
+
+  // 鑾峰彇鍙檵绾у弬璧涜�呭垪琛�
+  async getPromotableParticipants(currentStageId) {
+    try {
+      const variables = { currentStageId }
+      const data = await graphqlRequest(GET_PROMOTABLE_PARTICIPANTS, variables)
+      return data.promotableParticipants
+    } catch (error) {
+      console.error('鑾峰彇鍙檵绾у弬璧涜�呭垪琛ㄥけ璐�:', error)
+      throw error
+    }
+  },
+
+  // 鎵ц鏅嬬骇鎿嶄綔
+  async promoteParticipants(competitionId, participantIds, targetStageId) {
+    try {
+      const variables = {
+        input: {
+          competitionId,
+          participantIds,
+          targetStageId
+        }
+      }
+      const data = await graphqlRequest(PROMOTE_PARTICIPANTS, variables)
+      return data.promoteParticipants
+    } catch (error) {
+      console.error('鎵ц鏅嬬骇鎿嶄綔澶辫触:', error)
+      throw error
+    }
+  }
+}
+
+export default PromotionApi
\ No newline at end of file
diff --git a/web/src/api/rating.js b/web/src/api/rating.js
index a2a5d72..542ca97 100644
--- a/web/src/api/rating.js
+++ b/web/src/api/rating.js
@@ -1,138 +1,125 @@
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql'
+import { API_CONFIG, graphqlRequest } from '@/config/api'
 
-// GraphQL璇锋眰鍑芥暟
-async function graphqlRequest(query, variables = {}) {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query,
-      variables,
-    }),
-  })
+// 浣跨敤缁熶竴鐨凣raphQL璇锋眰鍑芥暟
 
-  if (!response.ok) {
-    throw new Error(`HTTP error! status: ${response.status}`)
+// GraphQL鏌ヨ璇彞
+const GET_RATING_SCHEMES_QUERY = `
+  query GetRatingSchemes($page: Int!, $size: Int!, $name: String) {
+    ratingSchemes(page: $page, size: $size, name: $name) {
+      content {
+        id
+        name
+        description
+        totalScore
+        state
+        stateName
+        createTime
+        updateTime
+      }
+      totalElements
+      page
+      size
+    }
   }
+`
 
-  const result = await response.json()
-  
-  if (result.errors) {
-    throw new Error(result.errors[0].message)
+const GET_ALL_RATING_SCHEMES_QUERY = `
+  query GetAllRatingSchemes {
+    allRatingSchemes {
+      id
+      name
+      description
+      totalScore
+      state
+      stateName
+    }
   }
+`
 
-  return result.data
-}
+const GET_RATING_SCHEME_QUERY = `
+  query GetRatingScheme($id: ID!) {
+    ratingScheme(id: $id) {
+      id
+      name
+      description
+      totalScore
+      state
+      stateName
+      createTime
+      updateTime
+      items {
+        id
+        name
+        maxScore
+        orderNo
+      }
+    }
+  }
+`
 
-// 鍒嗛〉鏌ヨ璇勫垎妯℃澘鍒楄〃
+const SAVE_RATING_SCHEME_MUTATION = `
+  mutation SaveRatingScheme($input: RatingSchemeInput!) {
+    saveRatingScheme(input: $input) {
+      id
+      name
+      description
+      totalScore
+      state
+      stateName
+      createTime
+      updateTime
+    }
+  }
+`
+
+const DELETE_RATING_SCHEME_MUTATION = `
+  mutation DeleteRatingScheme($id: ID!) {
+    deleteRatingScheme(id: $id)
+  }
+`
+
+// API鍑芥暟
 export const getRatingSchemes = async (page = 0, size = 10, name = '') => {
-  const query = `
-    query GetRatingSchemes($page: Int, $size: Int, $name: String) {
-      ratingSchemes(page: $page, size: $size, name: $name) {
-        content {
-          id
-          name
-          description
-          totalScore
-          createTime
-          updateTime
-        }
-        totalElements
-        totalPages
-        number
-        size
-        first
-        last
-      }
-    }
-  `
-  
-  const variables = { page, size }
-  if (name) {
-    variables.name = name
+  try {
+    const data = await graphqlRequest(GET_RATING_SCHEMES_QUERY, { page, size, name })
+    return data.ratingSchemes
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇璇勫垎鏂规鍒楄〃澶辫触')
   }
-  
-  const data = await graphqlRequest(query, variables)
-  return data.ratingSchemes
 }
 
-// 鏍规嵁ID鑾峰彇璇勫垎妯℃澘璇︽儏
-export const getRatingScheme = async (id) => {
-  const query = `
-    query GetRatingScheme($id: ID!) {
-      ratingScheme(id: $id) {
-        id
-        name
-        description
-        totalScore
-        items {
-          id
-          name
-          maxScore
-          orderNo
-        }
-        createTime
-        updateTime
-      }
-    }
-  `
-  
-  const data = await graphqlRequest(query, { id })
-  return data.ratingScheme
-}
-
-// 鑾峰彇鎵�鏈夎瘎鍒嗘ā鏉匡紙鐢ㄤ簬涓嬫媺閫夋嫨锛�
 export const getAllRatingSchemes = async () => {
-  const query = `
-    query GetAllRatingSchemes {
-      allRatingSchemes {
-        id
-        name
-        description
-        totalScore
-      }
-    }
-  `
-  
-  const data = await graphqlRequest(query)
-  return data.allRatingSchemes
+  try {
+    const data = await graphqlRequest(GET_ALL_RATING_SCHEMES_QUERY)
+    return data.allRatingSchemes || []
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇鎵�鏈夎瘎鍒嗘柟妗堝け璐�')
+  }
 }
 
-// 淇濆瓨璇勫垎妯℃澘
-export const saveRatingScheme = async (input) => {
-  const mutation = `
-    mutation SaveRatingScheme($input: RatingSchemeInput!) {
-      saveRatingScheme(input: $input) {
-        id
-        name
-        description
-        totalScore
-        items {
-          id
-          name
-          maxScore
-          orderNo
-        }
-        createTime
-        updateTime
-      }
-    }
-  `
-  
-  const data = await graphqlRequest(mutation, { input })
-  return data.saveRatingScheme
+export const getRatingScheme = async (id) => {
+  try {
+    const data = await graphqlRequest(GET_RATING_SCHEME_QUERY, { id })
+    return data.ratingScheme
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇璇勫垎鏂规璇︽儏澶辫触')
+  }
 }
 
-// 鍒犻櫎璇勫垎妯℃澘
+export const saveRatingScheme = async (ratingSchemeData) => {
+  try {
+    const data = await graphqlRequest(SAVE_RATING_SCHEME_MUTATION, { input: ratingSchemeData })
+    return data.saveRatingScheme
+  } catch (error) {
+    throw new Error(error.message || '淇濆瓨璇勫垎鏂规澶辫触')
+  }
+}
+
 export const deleteRatingScheme = async (id) => {
-  const mutation = `
-    mutation DeleteRatingScheme($id: ID!) {
-      deleteRatingScheme(id: $id)
-    }
-  `
-  
-  const data = await graphqlRequest(mutation, { id })
-  return data.deleteRatingScheme
+  try {
+    const data = await graphqlRequest(DELETE_RATING_SCHEME_MUTATION, { id })
+    return data.deleteRatingScheme
+  } catch (error) {
+    throw new Error(error.message || '鍒犻櫎璇勫垎鏂规澶辫触')
+  }
 }
\ No newline at end of file
diff --git a/web/src/api/region.js b/web/src/api/region.js
index e61e32d..87cbcdf 100644
--- a/web/src/api/region.js
+++ b/web/src/api/region.js
@@ -1,138 +1,159 @@
-const GRAPHQL_ENDPOINT = '/api/graphql'
+import { API_CONFIG, graphqlRequest } from '@/config/api'
 
-// GraphQL璇锋眰鍑芥暟
-async function graphqlRequest(query, variables = {}) {
-  const response = await fetch(GRAPHQL_ENDPOINT, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-    },
-    body: JSON.stringify({
-      query,
-      variables,
-    }),
-  })
+// GraphQL璇锋眰鍑芥暟 - 浣跨敤缁熶竴鐨刧raphqlRequest
 
-  if (!response.ok) {
-    const errorText = await response.text();
-    throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`)
-  }
-
-  const result = await response.json()
+// 鑾峰彇鎵�鏈夊湴鍖�
+export const getRegions = async () => {
+  const query = `
+    query GetRegions {
+      regions {
+        id
+        name
+        description
+        createTime
+        updateTime
+      }
+    }
+  `
   
-  if (result.errors) {
-    throw new Error(result.errors.map(e => e.message).join('\n'))
+  try {
+    const data = await graphqlRequest(query)
+    return data.regions || []
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇鍦板尯鍒楄〃澶辫触')
   }
-
-  return result.data
 }
 
-// --- GraphQL Query Strings ---
-
-const GET_REGIONS = `
-  query GetRegions($name: String, $state: Int, $page: Int!, $size: Int!) {
-    regions(name: $name, state: $state, page: $page, size: $size) {
-      content { id name pid code level leafFlag fullPath state createTime createUserId updateTime updateUserId version }
-      totalElements totalPages currentPage pageSize
+// 鏍规嵁ID鑾峰彇鍦板尯
+export const getRegion = async (id) => {
+  const query = `
+    query GetRegion($id: ID!) {
+      region(id: $id) {
+        id
+        name
+        description
+        createTime
+        updateTime
+      }
     }
+  `
+  
+  try {
+    const data = await graphqlRequest(query, { id })
+    return data.region
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇鍦板尯璇︽儏澶辫触')
   }
-`
+}
 
-const GET_ALL_REGIONS = `
-  query GetAllRegions {
-    allRegions { id name pid code level leafFlag fullPath state createTime }
+// 淇濆瓨鍦板尯锛堟柊澧炴垨鏇存柊锛�
+export const saveRegion = async (regionData) => {
+  const mutation = `
+    mutation SaveRegion($input: RegionInput!) {
+      saveRegion(input: $input) {
+        id
+        name
+        description
+        createTime
+        updateTime
+      }
+    }
+  `
+  
+  try {
+    const data = await graphqlRequest(mutation, { input: regionData })
+    return data.saveRegion
+  } catch (error) {
+    throw new Error(error.message || '淇濆瓨鍦板尯澶辫触')
   }
-`
+}
 
-const GET_REGION = `
-  query GetRegion($id: ID!) {
-    region(id: $id) { id name pid code level leafFlag fullPath state createTime createUserId updateTime updateUserId version }
+// 鍒犻櫎鍦板尯
+export const deleteRegion = async (id) => {
+  const mutation = `
+    mutation DeleteRegion($id: ID!) {
+      deleteRegion(id: $id)
+    }
+  `
+  
+  try {
+    const data = await graphqlRequest(mutation, { id })
+    return data.deleteRegion
+  } catch (error) {
+    throw new Error(error.message || '鍒犻櫎鍦板尯澶辫触')
   }
-`
+}
 
-const GET_PROVINCES = `
-  query GetProvinces {
-    provinces { id name pid code level state }
+// 鑾峰彇鎵�鏈夌渷浠�
+export const getProvinces = async () => {
+  const query = `
+    query GetProvinces {
+      provinces {
+        id
+        name
+        pid
+        level
+      }
+    }
+  `
+  
+  try {
+    const data = await graphqlRequest(query)
+    return data.provinces || []
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇鐪佷唤鍒楄〃澶辫触')
   }
-`
+}
 
-const GET_CITIES = `
-  query GetCities($provinceId: ID!) {
-    cities(provinceId: $provinceId) { id name pid code level state }
+// 鏍规嵁鐪佷唤ID鑾峰彇鍩庡競
+export const getCities = async (provinceId) => {
+  const query = `
+    query GetCities($provinceId: ID!) {
+      cities(provinceId: $provinceId) {
+        id
+        name
+        pid
+        level
+      }
+    }
+  `
+  
+  try {
+    const data = await graphqlRequest(query, { provinceId })
+    return data.cities || []
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇鍩庡競鍒楄〃澶辫触')
   }
-`
+}
 
-const GET_DISTRICTS = `
-  query GetDistricts($cityId: ID!) {
-    districts(cityId: $cityId) { id name pid code level state }
+// 鏍规嵁鍩庡競ID鑾峰彇鍖哄幙
+export const getDistricts = async (cityId) => {
+  const query = `
+    query GetDistricts($cityId: ID!) {
+      districts(cityId: $cityId) {
+        id
+        name
+        pid
+        level
+      }
+    }
+  `
+  
+  try {
+    const data = await graphqlRequest(query, { cityId })
+    return data.districts || []
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇鍖哄幙鍒楄〃澶辫触')
   }
-`
+}
 
-const GET_REGION_CHILDREN = `
-  query GetRegionChildren($parentId: ID!) {
-    regionChildren(parentId: $parentId) { id name pid code level leafFlag state }
-  }
-`
-
-const SAVE_REGION = `
-  mutation SaveRegion($input: RegionInput!) {
-    saveRegion(input: $input) { id name pid code level leafFlag fullPath state createTime updateTime version }
-  }
-`
-
-const DELETE_REGION = `
-  mutation DeleteRegion($id: ID!) {
-    deleteRegion(id: $id)
-  }
-`
-
-const TOGGLE_REGION_STATE = `
-  mutation ToggleRegionState($id: ID!) {
-    toggleRegionState(id: $id) { id name state updateTime }
-  }
-`
-
-// --- API Functions ---
-
+// RegionApi 瀵硅薄锛屽寘鍚墍鏈夊尯鍩熺浉鍏崇殑API鏂规硶
 export const RegionApi = {
-  getRegions: async (name, state, page, size) => {
-    const data = await graphqlRequest(GET_REGIONS, { name, state, page, size });
-    return data.regions;
-  },
-  getAllRegions: async () => {
-    const data = await graphqlRequest(GET_ALL_REGIONS);
-    return data.allRegions;
-  },
-  getRegion: async (id) => {
-    const data = await graphqlRequest(GET_REGION, { id });
-    return data.region;
-  },
-  getProvinces: async () => {
-    const data = await graphqlRequest(GET_PROVINCES);
-    return data.provinces || [];
-  },
-  getCities: async (provinceId) => {
-    const data = await graphqlRequest(GET_CITIES, { provinceId });
-    return data.cities || [];
-  },
-  getDistricts: async (cityId) => {
-    const data = await graphqlRequest(GET_DISTRICTS, { cityId });
-    return data.districts || [];
-  },
-  getChildren: async (parentId) => {
-    const data = await graphqlRequest(GET_REGION_CHILDREN, { parentId });
-    return data.regionChildren;
-  },
-  saveRegion: async (input) => {
-    const data = await graphqlRequest(SAVE_REGION, { input });
-    return data.saveRegion;
-  },
-  deleteRegion: async (id) => {
-    const data = await graphqlRequest(DELETE_REGION, { id });
-    return data.deleteRegion;
-  },
-  toggleRegionState: async (id) => {
-    const data = await graphqlRequest(TOGGLE_REGION_STATE, { id });
-    return data.toggleRegionState;
-  }
+  getRegions,
+  getRegion,
+  saveRegion,
+  deleteRegion,
+  getProvinces,
+  getCities,
+  getDistricts
 }
\ No newline at end of file
diff --git a/web/src/api/role.ts b/web/src/api/role.ts
index 46aed90..434ee33 100644
--- a/web/src/api/role.ts
+++ b/web/src/api/role.ts
@@ -1,5 +1,7 @@
 // 瑙掕壊绠$悊API
 
+import { API_CONFIG, graphqlRequest } from '@/config/api'
+
 // 瑙掕壊鐩稿叧鐨� GraphQL 鏌ヨ
 export const ROLE_QUERIES = {
   // 鑾峰彇鎵�鏈夎鑹�
@@ -78,19 +80,19 @@
   `,
   
   // 鏍规嵁鍚嶇О妯$硦鏌ヨ瑙掕壊
-  GET_ROLES_BY_NAME: `
-    query GetRolesByName($name: String!) {
-      rolesByName(name: $name) {
-        id
-        code
-        name
-        description
-        state
-        createTime
-        updateTime
-      }
-    }
-  `
+   SEARCH_ROLES_BY_NAME: `
+     query SearchRolesByName($name: String!) {
+       searchRolesByName(name: $name) {
+         id
+         code
+         name
+         description
+         state
+         createTime
+         updateTime
+       }
+     }
+   `
 }
 
 // 绫诲瀷瀹氫箟
@@ -109,136 +111,60 @@
   // 鑾峰彇鎵�鏈夎鑹�
   async getRoles(): Promise<Role[]> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: ROLE_QUERIES.GET_ROLES
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.roles || []
+      const data = await graphqlRequest(ROLE_QUERIES.GET_ROLES)
+      return data?.roles || []
     } catch (error: any) {
       throw new Error(error.message || '鑾峰彇瑙掕壊鍒楄〃澶辫触')
     }
   },
 
-  // 鑾峰彇鎵�鏈夊惎鐢ㄧ姸鎬佺殑瑙掕壊
+  // 鑾峰彇婵�娲荤姸鎬佺殑瑙掕壊
   async getActiveRoles(): Promise<Role[]> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: ROLE_QUERIES.GET_ACTIVE_ROLES
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.activeRoles || []
+      const data = await graphqlRequest(ROLE_QUERIES.GET_ACTIVE_ROLES)
+      return data?.activeRoles || []
     } catch (error: any) {
-      throw new Error(error.message || '鑾峰彇鍚敤瑙掕壊鍒楄〃澶辫触')
+      throw new Error(error.message || '鑾峰彇婵�娲昏鑹插垪琛ㄥけ璐�')
     }
   },
 
-  // 鏍规嵁ID鑾峰彇瑙掕壊璇︽儏
-  async getRoleById(id: string): Promise<Role | null> {
-    try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: ROLE_QUERIES.GET_ROLE,
-          variables: { id }
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.role || null
-    } catch (error: any) {
-      throw new Error(error.message || '鑾峰彇瑙掕壊璇︽儏澶辫触')
-    }
-  },
+  // 鏍规嵁ID鑾峰彇瑙掕壊
+   async getRoleById(id: string): Promise<Role | null> {
+     try {
+       const data = await graphqlRequest(ROLE_QUERIES.GET_ROLE, { id })
+       return data?.role || null
+     } catch (error: any) {
+       throw new Error(error.message || '鑾峰彇瑙掕壊璇︽儏澶辫触')
+     }
+   },
 
-  // 鏍规嵁瑙掕壊浠g爜鑾峰彇瑙掕壊
+  // 鏍规嵁浠g爜鑾峰彇瑙掕壊
   async getRoleByCode(code: string): Promise<Role | null> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: ROLE_QUERIES.GET_ROLE_BY_CODE,
-          variables: { code }
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.roleByCode || null
+      const data = await graphqlRequest(ROLE_QUERIES.GET_ROLE_BY_CODE, { code })
+      return data?.roleByCode || null
     } catch (error: any) {
-      throw new Error(error.message || '鏍规嵁浠g爜鑾峰彇瑙掕壊澶辫触')
+      throw new Error(error.message || '鑾峰彇瑙掕壊璇︽儏澶辫触')
     }
   },
 
   // 鏍规嵁鐘舵�佽幏鍙栬鑹�
   async getRolesByState(state: number): Promise<Role[]> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: ROLE_QUERIES.GET_ROLES_BY_STATE,
-          variables: { state }
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.rolesByState || []
+      const data = await graphqlRequest(ROLE_QUERIES.GET_ROLES_BY_STATE, { state })
+      return data?.rolesByState || []
     } catch (error: any) {
-      throw new Error(error.message || '鏍规嵁鐘舵�佽幏鍙栬鑹插け璐�')
+      throw new Error(error.message || '鑾峰彇瑙掕壊鍒楄〃澶辫触')
     }
   },
 
-  // 鏍规嵁鍚嶇О妯$硦鏌ヨ瑙掕壊
+  // 鏍规嵁鍚嶇О鎼滅储瑙掕壊
   async searchRolesByName(name: string): Promise<Role[]> {
     try {
-      const response = await fetch('http://localhost:8080/api/graphql', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify({
-          query: ROLE_QUERIES.GET_ROLES_BY_NAME,
-          variables: { name }
-        })
-      })
-      const result = await response.json()
-      if (result.errors) {
-        throw new Error(result.errors[0].message)
-      }
-      return result.data?.rolesByName || []
+      const data = await graphqlRequest(ROLE_QUERIES.SEARCH_ROLES_BY_NAME, { name })
+      return data?.searchRolesByName || []
     } catch (error: any) {
-      throw new Error(error.message || '鏍规嵁鍚嶇О鎼滅储瑙掕壊澶辫触')
+      throw new Error(error.message || '鎼滅储瑙掕壊澶辫触')
     }
   }
 }
\ No newline at end of file
diff --git a/web/src/components/PlayerInfoCard.vue b/web/src/components/PlayerInfoCard.vue
index 88f82e4..8e9765d 100644
--- a/web/src/components/PlayerInfoCard.vue
+++ b/web/src/components/PlayerInfoCard.vue
@@ -9,6 +9,8 @@
           :size="80" 
           :src="playerInfo.avatarUrl"
           :style="{ backgroundColor: avatarBgColor }"
+          class="clickable-avatar"
+          @click="handleAvatarClick"
         >
           {{ playerInfo.name ? playerInfo.name.charAt(0) : '?' }}
         </el-avatar>
@@ -35,13 +37,35 @@
         <p>{{ playerInfo.description }}</p>
       </div>
     </div>
+    
+    <!-- 澶村儚棰勮瀵硅瘽妗� -->
+    <el-dialog
+      v-model="previewVisible"
+      title="澶村儚棰勮"
+      width="500px"
+      :show-close="true"
+      center
+    >
+      <div class="avatar-preview-container">
+        <img 
+          v-if="playerInfo.avatarUrl" 
+          :src="playerInfo.avatarUrl" 
+          class="preview-image"
+          alt="澶村儚棰勮"
+        />
+        <div v-else class="no-avatar">
+          <el-icon size="80"><Picture /></el-icon>
+          <p>鏆傛棤澶村儚</p>
+        </div>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { computed } from 'vue'
-import { ElAvatar, ElIcon } from 'element-plus'
-import { Phone } from '@element-plus/icons-vue'
+import { computed, ref } from 'vue'
+import { ElAvatar, ElIcon, ElDialog } from 'element-plus'
+import { Phone, Picture } from '@element-plus/icons-vue'
 
 const props = defineProps({
   playerInfo: {
@@ -56,6 +80,16 @@
   }
 })
 
+// 鍝嶅簲寮忔暟鎹�
+const previewVisible = ref(false)
+
+// 澶村儚鐐瑰嚮澶勭悊
+const handleAvatarClick = () => {
+  if (props.playerInfo.avatarUrl) {
+    previewVisible.value = true
+  }
+}
+
 // 鏍规嵁濮撳悕鐢熸垚澶村儚鑳屾櫙鑹�
 const avatarBgColor = computed(() => {
   if (!props.playerInfo.name) return '#409EFF'
diff --git a/web/src/components/SubmissionFiles.vue b/web/src/components/SubmissionFiles.vue
index a3e9c38..be768b9 100644
--- a/web/src/components/SubmissionFiles.vue
+++ b/web/src/components/SubmissionFiles.vue
@@ -25,15 +25,20 @@
         </div>
         
         <!-- 瑙嗛棰勮 -->
-        <div v-else-if="isVideo(file)" class="video-preview">
-          <video 
-            :src="file.url" 
-            controls 
-            preload="metadata"
-            class="preview-video"
-          >
-            鎮ㄧ殑娴忚鍣ㄤ笉鏀寔瑙嗛鎾斁
-          </video>
+        <div v-else-if="isVideo(file)" class="video-preview" @click="playVideo(file)">
+          <div class="video-thumbnail">
+            <el-image 
+              :src="file.thumbUrl || file.url" 
+              :alt="file.name"
+              fit="cover"
+              class="preview-image"
+            />
+            <div class="play-overlay">
+              <el-icon :size="40" class="play-icon">
+                <VideoPlay />
+              </el-icon>
+            </div>
+          </div>
           <div class="file-info">
             <span class="file-name">{{ file.name }}</span>
             <span class="file-size">{{ formatFileSize(file.fileSize) }}</span>
@@ -54,10 +59,10 @@
             <el-button 
               type="primary" 
               size="small" 
-              @click="downloadFile(file)"
+              @click="previewOrDownloadFile(file)"
               class="download-btn"
             >
-              涓嬭浇
+              {{ isImage(file) || isVideo(file) ? '棰勮' : '涓嬭浇' }}
             </el-button>
           </div>
         </div>
@@ -67,13 +72,33 @@
     <div v-else class="no-files">
       <el-empty description="鏆傛棤鎻愪氦璧勬枡" :image-size="80" />
     </div>
+
+    <!-- 瑙嗛鎾斁瀵硅瘽妗� -->
+    <el-dialog
+      v-model="videoDialogVisible"
+      :title="currentVideoFile?.name || '瑙嗛鎾斁'"
+      width="80%"
+      center
+    >
+      <div class="video-player-container">
+        <video
+          v-if="currentVideoFile"
+          :src="currentVideoFile.url"
+          controls
+          autoplay
+          class="video-player"
+        >
+          鎮ㄧ殑娴忚鍣ㄤ笉鏀寔瑙嗛鎾斁
+        </video>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { computed } from 'vue'
-import { ElImage, ElButton, ElIcon, ElEmpty } from 'element-plus'
-import { Document, Files } from '@element-plus/icons-vue'
+import { computed, ref } from 'vue'
+import { ElImage, ElButton, ElIcon, ElEmpty, ElDialog } from 'element-plus'
+import { Document, Files, VideoPlay } from '@element-plus/icons-vue'
 
 const props = defineProps({
   files: {
@@ -81,6 +106,10 @@
     default: () => []
   }
 })
+
+// 瑙嗛鎾斁瀵硅瘽妗�
+const videoDialogVisible = ref(false)
+const currentVideoFile = ref(null)
 
 // 鍥剧墖鏂囦欢URL鍒楄〃锛堢敤浜庨瑙堬級
 const imageUrls = computed(() => {
@@ -123,15 +152,27 @@
   return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]
 }
 
-// 涓嬭浇鏂囦欢
-const downloadFile = (file) => {
-  const link = document.createElement('a')
-  link.href = file.url
-  link.download = file.name
-  link.target = '_blank'
-  document.body.appendChild(link)
-  link.click()
-  document.body.removeChild(link)
+// 鎾斁瑙嗛
+const playVideo = (file) => {
+  currentVideoFile.value = file
+  videoDialogVisible.value = true
+}
+
+// 棰勮鎴栦笅杞芥枃浠�
+const previewOrDownloadFile = (file) => {
+  if (isImage(file) || isVideo(file)) {
+    // 鍥剧墖鍜岃棰戝湪鏂扮獥鍙d腑棰勮
+    window.open(file.url, '_blank')
+  } else {
+    // 鍏朵粬鏂囦欢绫诲瀷涓嬭浇
+    const link = document.createElement('a')
+    link.href = file.url
+    link.download = file.name
+    link.target = '_blank'
+    document.body.appendChild(link)
+    link.click()
+    document.body.removeChild(link)
+  }
 }
 </script>
 
@@ -172,11 +213,37 @@
   flex-direction: column;
 }
 
-.preview-image,
-.preview-video {
+.preview-image {
   width: 100%;
   height: 120px;
   object-fit: cover;
+}
+
+.video-thumbnail {
+  position: relative;
+  width: 100%;
+  height: 120px;
+  cursor: pointer;
+}
+
+.play-overlay {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  background: rgba(0, 0, 0, 0.6);
+  border-radius: 50%;
+  padding: 12px;
+  transition: all 0.3s;
+}
+
+.play-overlay:hover {
+  background: rgba(0, 0, 0, 0.8);
+  transform: translate(-50%, -50%) scale(1.1);
+}
+
+.play-icon {
+  color: white;
 }
 
 .document-preview {
@@ -235,4 +302,17 @@
 .file-document {
   border-color: #E6A23C;
 }
+
+.video-player-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 300px;
+}
+
+.video-player {
+  width: 100%;
+  max-width: 800px;
+  height: auto;
+}
 </style>
\ No newline at end of file
diff --git a/web/src/config/api.ts b/web/src/config/api.ts
new file mode 100644
index 0000000..485abb2
--- /dev/null
+++ b/web/src/config/api.ts
@@ -0,0 +1,69 @@
+// API閰嶇疆鏂囦欢 - 缁熶竴绠$悊鎵�鏈堿PI鐩稿叧閰嶇疆
+export const API_CONFIG = {
+  // 鍩虹URL閰嶇疆
+  BASE_URL: '/api',
+  
+  // GraphQL绔偣
+  GRAPHQL_ENDPOINT: '/api/graphql',
+  
+  // 鍏朵粬API绔偣
+  ENDPOINTS: {
+    MEDIA_UPLOAD: '/api/media/upload',
+    MEDIA_DOWNLOAD: '/api/media/download',
+    WECHAT_LOGIN: '/api/wechat/login',
+    WECHAT_PHONE: '/api/wechat/phone'
+  }
+}
+
+// GraphQL璇锋眰宸ュ叿鍑芥暟
+export const graphqlRequest = async (query: string, variables: any = {}) => {
+  // 鑾峰彇JWT token
+  const { getToken } = await import('@/utils/auth');
+  const token = getToken();
+  const headers: Record<string, string> = {
+    'Content-Type': 'application/json',
+  };
+  if (token) {
+    headers['Authorization'] = `Bearer ${token}`;
+  }
+
+  const response = await fetch(API_CONFIG.GRAPHQL_ENDPOINT, {
+    method: 'POST',
+    headers: headers,
+    body: JSON.stringify({
+      query,
+      variables,
+    }),
+  })
+
+  if (!response.ok) {
+    throw new Error(`HTTP error! status: ${response.status}`)
+  }
+
+  const result = await response.json()
+  
+  if (result.errors) {
+    throw new Error(result.errors[0].message)
+  }
+
+  return result.data
+}
+
+// 閫氱敤API璇锋眰宸ュ叿鍑芥暟
+export const apiRequest = async (endpoint: string, options: RequestInit = {}) => {
+  const url = endpoint.startsWith('http') ? endpoint : `${API_CONFIG.BASE_URL}${endpoint}`
+  
+  const response = await fetch(url, {
+    headers: {
+      'Content-Type': 'application/json',
+      ...options.headers,
+    },
+    ...options,
+  })
+
+  if (!response.ok) {
+    throw new Error(`HTTP error! status: ${response.status}`)
+  }
+
+  return response.json()
+}
\ No newline at end of file
diff --git a/web/src/constants/mediaTargetType.ts b/web/src/constants/mediaTargetType.ts
index d19fadd..2a38e8e 100644
--- a/web/src/constants/mediaTargetType.ts
+++ b/web/src/constants/mediaTargetType.ts
@@ -7,7 +7,7 @@
   ACTIVITY: 2,               // 娲诲姩
   CAROUSEL: 4,               // 杞挱鍥�
   ACTIVITY_PLAYER_SUBMISSION: 5,  // 鍙傝禌鎶ュ悕璧勬枡
-  STUDENT_AVATAR: 6,         // 瀛﹀憳澶村儚
+
   USER_AVATAR: 7             // 鐢ㄦ埛澶村儚
 } as const;
 
@@ -21,6 +21,6 @@
   [MediaTargetType.ACTIVITY]: '娲诲姩',
   [MediaTargetType.CAROUSEL]: '杞挱鍥�',
   [MediaTargetType.ACTIVITY_PLAYER_SUBMISSION]: '鍙傝禌鎶ュ悕璧勬枡',
-  [MediaTargetType.STUDENT_AVATAR]: '瀛﹀憳澶村儚',
+
   [MediaTargetType.USER_AVATAR]: '鐢ㄦ埛澶村儚'
 } as const;
\ No newline at end of file
diff --git a/web/src/layout/index.vue b/web/src/layout/index.vue
index 75d787e..0d417e6 100644
--- a/web/src/layout/index.vue
+++ b/web/src/layout/index.vue
@@ -74,7 +74,9 @@
   { path: '/activity', title: '姣旇禌绠$悊', icon: 'Trophy' },
   { path: '/judge', title: '璇勫绠$悊', icon: 'User' },
   { path: '/rating-scheme', title: '璇勫垎妯℃澘', icon: 'Document' },
-  { path: '/player', title: '姣旇禌鎶ュ悕', icon: 'UserFilled' },
+  { path: '/player', title: '鎶ュ悕瀹℃牳', icon: 'UserFilled' },
+  { path: '/project-review', title: '椤圭洰璇勫', icon: 'View' },
+  { path: '/competition-promotion', title: '姣旇禌鏅嬬骇', icon: 'Promotion' },
   { path: '/carousel', title: '鏂伴椈涓庢帹骞�', icon: 'Picture' },
   { path: '/region', title: '鍖哄煙绠$悊', icon: 'Location' },
   { path: '/employee', title: '鍛樺伐绠$悊', icon: 'Avatar' }
diff --git a/web/src/router/index.ts b/web/src/router/index.ts
index 731a900..b052ee7 100644
--- a/web/src/router/index.ts
+++ b/web/src/router/index.ts
@@ -65,7 +65,13 @@
         path: '/player',
         name: 'Player',
         component: () => import('@/views/player/index.vue'),
-        meta: { title: '姣旇禌鎶ュ悕', icon: 'UserFilled' }
+        meta: { title: '鎶ュ悕瀹℃牳', icon: 'UserFilled' }
+      },
+      {
+        path: '/player/:id/detail',
+        name: 'PlayerDetail',
+        component: () => import('@/views/player/detail.vue'),
+        meta: { title: '鎶ュ悕璇︽儏', icon: 'UserFilled' }
       },
       {
         path: '/activity-player/:id/rating',
@@ -90,6 +96,42 @@
         name: 'Employee',
         component: () => import('@/views/employee/index.vue'),
         meta: { title: '鍛樺伐绠$悊', icon: 'Avatar' }
+      },
+      {
+        path: '/project-review',
+        name: 'ProjectReview',
+        component: () => import('@/views/project-review/index.vue'),
+        meta: { title: '椤圭洰璇勫', icon: 'View' }
+      },
+      {
+        path: '/project-review/:id/detail',
+        name: 'ProjectReviewDetail',
+        component: () => import('@/views/project-review/detail.vue'),
+        meta: { title: '椤圭洰璇勫璇︽儏', hidden: true }
+      },
+      {
+        path: '/review',
+        name: 'Review',
+        component: () => import('@/views/review/index.vue'),
+        meta: { title: '椤圭洰璇勫', icon: 'Edit' }
+      },
+      {
+        path: '/review/:id/detail',
+        name: 'ReviewDetail',
+        component: () => import('@/views/review/detail.vue'),
+        meta: { title: '椤圭洰璇勫璇︽儏', hidden: true }
+      },
+      {
+        path: '/competition-promotion',
+        name: 'CompetitionPromotion',
+        component: () => import('@/views/competition-promotion/index.vue'),
+        meta: { title: '姣旇禌鏅嬬骇', icon: 'Promotion' }
+      },
+      {
+        path: '/test/graphql',
+        name: 'GraphQLTest',
+        component: () => import('@/views/test/graphql-test.vue'),
+        meta: { title: 'GraphQL娴嬭瘯', icon: 'Connection' }
       }
     ]
   },
diff --git a/web/src/utils/appConfig.js b/web/src/utils/appConfig.js
index ebfb020..aea2f43 100644
--- a/web/src/utils/appConfig.js
+++ b/web/src/utils/appConfig.js
@@ -1,4 +1,4 @@
-const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql';
+const GRAPHQL_ENDPOINT = '/api/graphql';
 
 const GET_APP_CONFIG = `
   query AppConfig {
diff --git a/web/src/utils/graphql.ts b/web/src/utils/graphql.ts
index 4641b2d..e37ecdf 100644
--- a/web/src/utils/graphql.ts
+++ b/web/src/utils/graphql.ts
@@ -97,7 +97,6 @@
         name: string
         phone?: string
         description?: string
-        auditState?: number
       }
     }
   }
@@ -131,7 +130,6 @@
             name
             phone
             description
-            auditState
           }
         }
       }
diff --git a/web/src/views/ActivityDetail.vue b/web/src/views/ActivityDetail.vue
index 14f60e2..886b867 100644
--- a/web/src/views/ActivityDetail.vue
+++ b/web/src/views/ActivityDetail.vue
@@ -21,8 +21,6 @@
           <el-descriptions-item label="鎶ュ悕鎴鏃堕棿">{{ formatDateTime(activity.signupDeadline) }}</el-descriptions-item>
           <el-descriptions-item label="姣旇禌寮�濮嬫椂闂�">{{ formatDateTime(activity.matchTime) }}</el-descriptions-item>
           <el-descriptions-item label="姣旇禌鍦板潃">{{ activity.address || '-' }}</el-descriptions-item>
-          <el-descriptions-item label="浜烘暟">{{ activity.playerMax || '-' }}</el-descriptions-item>
-          <el-descriptions-item label="褰撳墠鎶ュ悕浜烘暟">{{ activity.playerCount || 0 }}</el-descriptions-item>
           <el-descriptions-item label="璇勫垎妯℃澘">
             {{ activity.ratingScheme ? activity.ratingScheme.name : '-' }}
           </el-descriptions-item>
@@ -38,26 +36,17 @@
         <!-- 姣旇禌闃舵 -->
         <div v-if="activity.stages && activity.stages.length > 0" class="stages-section">
           <h3>姣旇禌闃舵</h3>
-          <el-table :data="activity.stages" style="width: 100%">
-            <el-table-column prop="name" label="闃舵鍚嶇О" min-width="150" />
+          <el-table :data="sortedStages" style="width: 100%" table-layout="auto">
+            <el-table-column prop="sortOrder" label="椤哄簭" width="80" align="center" />
+            <el-table-column prop="name" label="闃舵鍚嶇О" width="200" show-overflow-tooltip />
             <el-table-column prop="matchTime" label="寮�濮嬫椂闂�" width="180">
               <template #default="{ row }">
                 {{ formatDateTime(row.matchTime) }}
               </template>
             </el-table-column>
-            <el-table-column prop="address" label="鍦板潃" min-width="150">
+            <el-table-column prop="address" label="鍦板潃" min-width="120" show-overflow-tooltip>
               <template #default="{ row }">
                 {{ row.address || '-' }}
-              </template>
-            </el-table-column>
-            <el-table-column prop="playerMax" label="鏈�澶т汉鏁�" width="100">
-              <template #default="{ row }">
-                {{ row.playerMax || '-' }}
-              </template>
-            </el-table-column>
-            <el-table-column prop="playerCount" label="瀹為檯浜烘暟" width="100">
-              <template #default="{ row }">
-                {{ row.playerCount || 0 }}
               </template>
             </el-table-column>
             <el-table-column prop="stateName" label="鐘舵��" width="100">
@@ -65,7 +54,7 @@
                 <el-tag :type="getStateType(row.state)">{{ row.stateName }}</el-tag>
               </template>
             </el-table-column>
-            <el-table-column label="鎿嶄綔" width="200">
+            <el-table-column label="鎿嶄綔" width="220" fixed="right">
               <template #default="{ row }">
                 <el-button size="small" @click="viewStageDetail(row)">鏌ョ湅璇︽儏</el-button>
                 <el-button size="small" type="warning" @click="closeStage(row)" v-if="row.state === 1">鍏抽棴</el-button>
@@ -121,7 +110,6 @@
           </el-descriptions-item>
           <el-descriptions-item label="寮�濮嬫椂闂�">{{ formatDateTime(selectedStage.matchTime) }}</el-descriptions-item>
           <el-descriptions-item label="鍦板潃">{{ selectedStage.address || '-' }}</el-descriptions-item>
-          <el-descriptions-item label="浜烘暟">{{ selectedStage.playerMax || '-' }}</el-descriptions-item>
           <el-descriptions-item label="璇勫垎妯℃澘">
             {{ selectedStage.ratingScheme ? selectedStage.ratingScheme.name : '缁ф壙姣旇禌妯℃澘' }}
           </el-descriptions-item>
@@ -137,7 +125,7 @@
 </template>
 
 <script setup>
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, computed } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { getActivity } from '@/api/activity'
@@ -150,6 +138,16 @@
 const activity = ref(null)
 const stageDialogVisible = ref(false)
 const selectedStage = ref(null)
+
+// 璁$畻灞炴��
+const sortedStages = computed(() => {
+  if (!activity.value || !activity.value.stages) return []
+  return [...activity.value.stages].sort((a, b) => {
+    const orderA = a.sortOrder || 999
+    const orderB = b.sortOrder || 999
+    return orderA - orderB
+  })
+})
 
 // 鍔犺浇姣旇禌璇︽儏
 const loadActivity = async () => {
@@ -233,9 +231,17 @@
 }
 
 const getStageName = (stageId) => {
-  if (!activity.value || !activity.value.stages) return '鏈煡闃舵'
-  const stage = activity.value.stages.find(s => s.id === stageId)
-  return stage ? stage.name : '鏈煡闃舵'
+  if (!activity.value) return '鏈煡闃舵'
+  
+  // 鍙鏌ユ瘮璧涢樁娈�
+  if (activity.value.stages) {
+    const stage = activity.value.stages.find(s => s.id === stageId)
+    if (stage) {
+      return stage.name
+    }
+  }
+  
+  return '鏈煡闃舵'
 }
 
 // 鐢熷懡鍛ㄦ湡
diff --git a/web/src/views/ActivityForm.vue b/web/src/views/ActivityForm.vue
index 140c631..5cadcfc 100644
--- a/web/src/views/ActivityForm.vue
+++ b/web/src/views/ActivityForm.vue
@@ -65,19 +65,9 @@
           </el-col>
         </el-row>
 
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="姣旇禌鍦板潃" prop="address">
-              <el-input v-model="form.address" placeholder="璇疯緭鍏ユ瘮璧涘湴鍧�" />
-            </el-form-item>
-          </el-col>
-          
-          <el-col :span="12">
-            <el-form-item label="浜烘暟" prop="playerMax">
-              <el-input-number v-model="form.playerMax" :min="1" :max="9999" style="width: 100%" />
-            </el-form-item>
-          </el-col>
-        </el-row>
+        <el-form-item label="姣旇禌鍦板潃" prop="address">
+          <el-input v-model="form.address" placeholder="璇疯緭鍏ユ瘮璧涘湴鍧�" />
+        </el-form-item>
 
         <el-form-item label="姣旇禌鎻忚堪" prop="description">
           <el-input
@@ -146,35 +136,33 @@
             <el-tab-pane label="姣旇禌闃舵" name="stages">
               <div class="stages-header">
                 <span>姣旇禌闃舵</span>
-                <el-button size="small" type="primary" @click="addStage">娣诲姞闃舵</el-button>
+                <div class="stages-controls">
+                  <el-button size="small" type="primary" @click="addStage">娣诲姞闃舵</el-button>
+                </div>
               </div>
               
               <div v-if="form.stages && form.stages.length > 0" class="stages-list">
-                <div v-for="(stage, index) in form.stages" :key="index" class="stage-item">
+                <div v-for="(stage, index) in sortedFormStages" :key="index" class="stage-item">
                   <div class="stage-info">
-                    <div class="stage-name">{{ stage.name || '鏈懡鍚嶉樁娈�' }}</div>
+                    <div class="stage-header">
+                      <span class="stage-order">{{ stage.sortOrder || '-' }}</span>
+                      <span class="stage-name">{{ stage.name || '鏈懡鍚嶉樁娈�' }}</span>
+                    </div>
                     <div class="stage-details">
                       <span class="detail-item">
                         <el-icon><Clock /></el-icon>
                         {{ formatDateTime(stage.matchTime) }}
                       </span>
-                      <span class="detail-item">
-                        <el-icon><User /></el-icon>
-                        {{ stage.playerMax || 0 }} 浜�
-                      </span>
-                      <span class="detail-item">
-                        <el-icon><UserFilled /></el-icon>
-                        瀹為檯: {{ stage.actualPlayerCount || 0 }} 浜�
-                      </span>
+
                       <el-tag :type="stage.state === 1 ? 'success' : 'info'" size="small">
                         {{ stage.state === 1 ? '杩涜涓�' : '鏈紑濮�' }}
                       </el-tag>
                     </div>
                   </div>
                   <div class="stage-actions">
-                    <el-button size="small" @click="editStage(stage, index)">缂栬緫</el-button>
+                    <el-button size="small" @click="editStage(stage, getOriginalStageIndex(stage))">缂栬緫</el-button>
                     <el-button size="small" @click="closeStage(stage)" v-if="stage.state === 1">鍏抽棴</el-button>
-                    <el-button size="small" type="danger" @click="removeStage(index)">鍒犻櫎</el-button>
+                    <el-button size="small" type="danger" @click="removeStage(getOriginalStageIndex(stage))">鍒犻櫎</el-button>
                   </div>
                 </div>
               </div>
@@ -214,37 +202,7 @@
               <el-empty v-if="!form.judges || form.judges.length === 0" description="鏆傛棤璇勫" />
             </el-tab-pane>
 
-            <!-- 瀛﹀憳鍒楄〃 -->
-            <el-tab-pane label="瀛﹀憳鍒楄〃" name="students">
-              <div class="students-header">
-                <span>瀛﹀憳鍒楄〃</span>
-              </div>
-              
-              <el-table :data="form.students" style="width: 100%" border>
-                <el-table-column label="瀛﹀憳鍚嶇О" prop="name" />
-                <el-table-column label="鏈�鍚庡弬涓庣殑姣旇禌闃舵" width="200">
-                  <template #default="{ row }">
-                    {{ getLastStage(row) }}
-                  </template>
-                </el-table-column>
-                <el-table-column label="鎿嶄綔" width="250" align="center">
-                  <template #default="{ row, $index }">
-                    <el-button size="small" @click="viewStudent(row, $index)">鏌ョ湅</el-button>
-                    <el-button size="small" type="primary" @click="rateStudent(row, $index)">璇勫垎</el-button>
-                    <el-button size="small" @click="commentStudent(row, $index)">鐐硅瘎</el-button>
-                    <el-button 
-                      size="small" 
-                      :type="row.isAdvanced ? 'success' : 'warning'"
-                      @click="toggleAdvancement(row, $index)"
-                    >
-                      {{ row.isAdvanced ? '宸叉檵绾�' : '鏅嬬骇' }}
-                    </el-button>
-                  </template>
-                </el-table-column>
-              </el-table>
-              
-              <el-empty v-if="!form.students || form.students.length === 0" description="鏆傛棤瀛﹀憳" />
-            </el-tab-pane>
+
           </el-tabs>
         </div>
 
@@ -274,6 +232,26 @@
           <el-input v-model="currentStage.name" placeholder="璇疯緭鍏ラ樁娈靛悕绉�" maxlength="30" />
         </el-form-item>
         
+        <el-form-item label="姣旇禌闃舵椤哄簭" prop="sortOrder">
+          <el-select v-model="currentStage.sortOrder" placeholder="璇烽�夋嫨闃舵椤哄簭" style="width: 100%">
+            <el-option label="1" :value="1" />
+            <el-option label="2" :value="2" />
+            <el-option label="3" :value="3" />
+            <el-option label="4" :value="4" />
+            <el-option label="5" :value="5" />
+          </el-select>
+        </el-form-item>
+        
+        <el-form-item label="瀛﹀憳浜烘暟" prop="playerMax">
+          <el-input-number 
+            v-model="currentStage.playerMax" 
+            :min="1" 
+            :max="1000"
+            placeholder="璇疯緭鍏ュ鍛樹汉鏁�"
+            style="width: 100%"
+          />
+        </el-form-item>
+        
         <el-form-item label="璇勫垎妯℃澘">
           <el-select v-model="currentStage.ratingSchemeId" placeholder="缁ф壙姣旇禌妯℃澘" style="width: 100%">
             <el-option label="缁ф壙姣旇禌妯℃澘" :value="null" />
@@ -299,10 +277,6 @@
         
         <el-form-item label="闃舵鍦板潃">
           <el-input v-model="currentStage.address" placeholder="璇疯緭鍏ラ樁娈靛湴鍧�" />
-        </el-form-item>
-        
-        <el-form-item label="浜烘暟">
-          <el-input-number v-model="currentStage.playerMax" :min="1" :max="9999" style="width: 100%" />
         </el-form-item>
         
         <el-form-item label="闃舵鎻忚堪">
@@ -349,7 +323,7 @@
         <div style="margin-bottom: 16px;">
           <el-form-item label="娣诲姞鍒伴樁娈碉細" label-width="100px">
             <el-select v-model="selectedStageOption" style="width: 100%;" @change="handleStageChange">
-              <el-option label="鎵�鏈夐樁娈�" value="all" />
+              <!-- 鍙樉绀烘瘮璧涢樁娈� -->
               <el-option 
                 v-for="stage in form.stages" 
                 :key="stage.id" 
@@ -462,6 +436,9 @@
 // Tab鐩稿叧
 const activeTab = ref('stages')
 
+// 闃舵鏁伴噺閫夋嫨
+const selectedStageCount = ref(1)
+
 // 闃舵缂栬緫寮圭獥鐩稿叧
 const stageDialogVisible = ref(false)
 const currentStageIndex = ref(-1)
@@ -472,7 +449,6 @@
   matchTime: '',
   address: '',
   ratingSchemeId: null,
-  playerMax: null,
   state: 1,
   actualPlayerCount: 0
 })
@@ -524,7 +500,7 @@
   matchTime: '',
   address: '',
   ratingSchemeId: null,
-  playerMax: 100,
+  playerMax: null,
   state: 1,
   stages: [],
   judges: [],
@@ -534,6 +510,16 @@
 
 // 璁$畻灞炴��
 const isEdit = computed(() => !!route.params.id)
+
+// 鎸塻ortOrder鎺掑簭鐨勯樁娈靛垪琛�
+const sortedFormStages = computed(() => {
+  if (!form.value.stages) return []
+  return [...form.value.stages].sort((a, b) => {
+    const orderA = a.sortOrder || 999
+    const orderB = b.sortOrder || 999
+    return orderA - orderB
+  })
+})
 
 // 琛ㄥ崟楠岃瘉瑙勫垯
 const rules = {
@@ -553,6 +539,10 @@
   name: [
     { required: true, message: '璇疯緭鍏ラ樁娈靛悕绉�', trigger: 'blur' },
     { max: 30, message: '闃舵鍚嶇О涓嶈兘瓒呰繃30涓瓧绗�', trigger: 'blur' }
+  ],
+  playerMax: [
+    { required: true, message: '璇疯緭鍏ュ鍛樹汉鏁�', trigger: 'blur' },
+    { type: 'number', min: 1, max: 1000, message: '瀛﹀憳浜烘暟蹇呴』鍦�1-1000涔嬮棿', trigger: 'blur' }
   ]
 }
 
@@ -599,7 +589,7 @@
         matchTime: activity.matchTime || '',
         address: activity.address || '',
         ratingSchemeId: activity.ratingSchemeId,
-        playerMax: activity.playerMax || 100,
+        playerMax: activity.playerMax,
         state: activity.state,
         stages: activity.stages || [],
         judges: activity.judges || [],
@@ -629,6 +619,9 @@
           return mediaItem
         })
         console.log('鏈�缁堢殑mediaFiles:', form.value.mediaFiles)
+      
+      // 璁剧疆闃舵鏁伴噺閫夋嫨鍣ㄧ殑鍊�
+      selectedStageCount.value = form.value.stages.length || 1
       } catch (e) {
         console.error('鍔犺浇娲诲姩濯掍綋澶辫触:', e)
       }
@@ -642,6 +635,45 @@
 }
 
 // 闃舵绠$悊
+// 闃舵鏁伴噺鍙樺寲澶勭悊
+const onStageCountChange = (count) => {
+  if (!count) return
+  
+  // 濡傛灉褰撳墠闃舵鏁伴噺灏戜簬閫夋嫨鐨勬暟閲忥紝鑷姩娣诲姞闃舵
+  while (form.value.stages.length < count) {
+    const stageIndex = form.value.stages.length + 1
+    form.value.stages.push({
+      id: null,
+      name: getDefaultStageName(stageIndex),
+      description: '',
+      matchTime: '',
+      address: form.value.address || '',
+      ratingSchemeId: form.value.ratingSchemeId,
+      sortOrder: stageIndex,
+      state: 1,
+      actualPlayerCount: 0
+    })
+  }
+  
+  // 濡傛灉褰撳墠闃舵鏁伴噺澶氫簬閫夋嫨鐨勬暟閲忥紝鍒犻櫎澶氫綑鐨勯樁娈�
+  if (form.value.stages.length > count) {
+    form.value.stages = form.value.stages.slice(0, count)
+  }
+  
+  ElMessage.success(`宸茶缃负${count}涓樁娈礰)
+}
+
+// 鑾峰彇榛樿闃舵鍚嶇О
+const getDefaultStageName = (index) => {
+  const stageNames = ['', '娴烽��', '澶嶈禌', '鍗婂喅璧�', '鍐宠禌', '鎬诲喅璧�']
+  return stageNames[index] || `绗�${index}闃舵`
+}
+
+// 鑾峰彇闃舵鍦ㄥ師濮嬫暟缁勪腑鐨勭储寮�
+const getOriginalStageIndex = (stage) => {
+  return form.value.stages.findIndex(s => s === stage)
+}
+
 const addStage = () => {
   currentStageIndex.value = -1
   resetStageForm()
@@ -662,6 +694,15 @@
       type: 'warning'
     })
     form.value.stages.splice(index, 1)
+    
+    // 閲嶆柊鎺掑簭sortOrder
+    form.value.stages.forEach((stage, idx) => {
+      stage.sortOrder = idx + 1
+    })
+    
+    // 鏇存柊閫夋嫨鐨勯樁娈垫暟閲�
+    selectedStageCount.value = form.value.stages.length
+    
     ElMessage.success('鍒犻櫎鎴愬姛')
   } catch {
     // 鐢ㄦ埛鍙栨秷鍒犻櫎
@@ -687,8 +728,10 @@
     await stageFormRef.value.validate()
     
     if (currentStageIndex.value === -1) {
-      // 鏂板闃舵
-      form.value.stages.push({ ...currentStage.value })
+      // 鏂板闃舵 - 璁剧疆姝g‘鐨剆ortOrder
+      const newStage = { ...currentStage.value }
+      newStage.sortOrder = form.value.stages.length + 1
+      form.value.stages.push(newStage)
     } else {
       // 缂栬緫闃舵
       form.value.stages[currentStageIndex.value] = { ...currentStage.value }
@@ -710,6 +753,7 @@
     address: '',
     ratingSchemeId: null,
     playerMax: null,
+    sortOrder: null, // 灏嗗湪saveStage涓缃纭殑鍊�
     state: 1,
     actualPlayerCount: 0
   }
@@ -756,13 +800,30 @@
 }
 
 const getJudgeStages = (judge) => {
-  if (!judge.stageIds || !form.value.stages) return []
-  return form.value.stages.filter(stage => judge.stageIds.includes(stage.id))
+  if (!judge.stageIds) return []
+  
+  const stages = []
+  
+  // 鍙鏌ユ瘮璧涢樁娈�
+  if (form.value.stages) {
+    const matchedStages = form.value.stages.filter(stage => judge.stageIds.includes(stage.id))
+    matchedStages.forEach(stage => {
+      stages.push({
+        id: stage.id,
+        name: stage.name
+      })
+    })
+  }
+  
+  return stages
 }
 
 const resetJudgeDialog = () => {
   judgeSearchText.value = ''
-  selectedStageOption.value = 'all'
+  // 榛樿閫夋嫨绗竴涓樁娈�
+  selectedStageOption.value = form.value.stages && form.value.stages.length > 0 
+    ? form.value.stages[0].id?.toString() || '' 
+    : ''
   selectedJudges.value = []
 }
 
@@ -806,22 +867,18 @@
       const existingJudge = form.value.judges.find(j => j.id === judgeId)
       if (existingJudge) {
         // 鏇存柊鐜版湁璇勫鐨勯樁娈�
-        if (selectedStageOption.value === 'all') {
-          existingJudge.stageIds = form.value.stages.map(s => s.id).filter(id => id != null)
-        } else {
-          const stageId = parseInt(selectedStageOption.value)
-          if (!existingJudge.stageIds.includes(stageId)) {
-            existingJudge.stageIds.push(stageId)
-          }
+        const stageId = parseInt(selectedStageOption.value)
+        if (!existingJudge.stageIds.includes(stageId)) {
+          existingJudge.stageIds.push(stageId)
         }
       } else {
         // 娣诲姞鏂拌瘎濮�
+        const stageIds = [parseInt(selectedStageOption.value)]
+        
         const newJudge = {
           id: judge.id,
           name: judge.name,
-          stageIds: selectedStageOption.value === 'all' 
-            ? form.value.stages.map(s => s.id).filter(id => id != null)
-            : [parseInt(selectedStageOption.value)]
+          stageIds: stageIds
         }
         form.value.judges.push(newJudge)
         addedCount++
@@ -1084,6 +1141,7 @@
         address: stage.address,
         ratingSchemeId: stage.ratingSchemeId,
         playerMax: stage.playerMax,
+        sortOrder: stage.sortOrder,
         state: stage.state || 1
       })) : [],
       judges: form.value.judges ? form.value.judges.map(judge => ({
@@ -1134,6 +1192,11 @@
   await loadRatingSchemes()
   await loadAllJudges()
   await loadActivity()
+  
+  // 濡傛灉鏄柊寤烘ā寮忎笖娌℃湁闃舵锛岃嚜鍔ㄥ垱寤轰竴涓樁娈�
+  if (!isEdit.value && form.value.stages.length === 0) {
+    onStageCountChange(1)
+  }
 })
 </script>
 
@@ -1169,7 +1232,7 @@
 
 .stage-header {
   display: flex;
-  justify-content: space-between;
+  justify-content: flex-start;
   align-items: center;
 }
 
@@ -1271,11 +1334,32 @@
   flex: 1;
 }
 
+.stage-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 8px;
+}
+
+.stage-order {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  height: 24px;
+  background-color: #409eff;
+  color: white;
+  border-radius: 50%;
+  font-size: 12px;
+  font-weight: 600;
+  flex-shrink: 0;
+}
+
 .stage-name {
   font-size: 16px;
   font-weight: 500;
   color: #303133;
-  margin-bottom: 8px;
+  margin: 0;
+  margin-left: 4px;
 }
 
 .stage-details {
diff --git a/web/src/views/competition-promotion/index.vue b/web/src/views/competition-promotion/index.vue
new file mode 100644
index 0000000..d2cebc3
--- /dev/null
+++ b/web/src/views/competition-promotion/index.vue
@@ -0,0 +1,727 @@
+<template>
+  <div class="promotion-container">
+    <el-card>
+      <template #header>
+        <div class="card-header">
+          <h3 class="card-title">姣旇禌鏅嬬骇绠$悊</h3>
+        </div>
+      </template>
+
+      <!-- 鎼滅储鏍� -->
+      <el-form :inline="true" class="search-form">
+        <el-form-item label="姣旇禌鍚嶇О">
+          <el-input
+            v-model="searchForm.name"
+            placeholder="璇疯緭鍏ユ瘮璧涘悕绉�"
+            @keyup.enter="loadData"
+            style="width: 200px"
+            clearable
+          />
+        </el-form-item>
+        
+        <el-form-item>
+          <el-button type="primary" @click="loadData" :loading="loading">
+            鎼滅储
+          </el-button>
+          <el-button @click="resetSearch">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+
+      <!-- 鏁版嵁琛ㄦ牸 -->
+      <el-table 
+        :data="competitions" 
+        style="width: 100%" 
+        v-loading="loading"
+        empty-text="鏆傛棤姣旇禌鏁版嵁"
+      >
+        <el-table-column prop="competitionName" label="姣旇禌鍚嶇О" min-width="150">
+          <template #default="scope">
+            <div class="competition-info">
+              <div class="main-name">{{ scope.row.competitionName }}</div>
+              <div class="stage-name">{{ scope.row.stageName }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="maxParticipants" label="鏈�澶т汉鏁�" width="100" align="center">
+          <template #default="scope">
+            <el-tag type="info">{{ scope.row.maxParticipants || '涓嶉檺' }}</el-tag>
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="currentCount" label="褰撳墠鏁伴噺" width="100" align="center">
+          <template #default="scope">
+            <el-button 
+              type="text" 
+              @click="viewParticipants(scope.row)"
+              class="count-link"
+            >
+              {{ scope.row.currentCount }}
+            </el-button>
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="status" label="鐘舵��" width="100" align="center">
+          <template #default="scope">
+            <el-tag :type="getStatusType(scope.row.status)">
+              {{ getStatusText(scope.row.status) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="startTime" label="寮�濮嬫椂闂�" width="180">
+          <template #default="scope">
+            {{ formatDate(scope.row.startTime) }}
+          </template>
+        </el-table-column>
+        
+        <el-table-column prop="endTime" label="缁撴潫鏃堕棿" width="180">
+          <template #default="scope">
+            {{ formatDate(scope.row.endTime) }}
+          </template>
+        </el-table-column>
+        
+        <el-table-column label="鎿嶄綔" width="150" fixed="right">
+          <template #default="scope">
+            <!-- 鍙湁闈炵涓�闃舵鎵嶆樉绀烘檵绾ф寜閽� -->
+            <el-button 
+              v-if="scope.row.sortOrder > 1"
+              type="primary" 
+              size="small" 
+              @click="selectPromotionCandidates(scope.row)"
+              :disabled="scope.row.state !== 1"
+            >
+              閫夋嫨鏅嬬骇浜哄憳
+            </el-button>
+            <!-- 绗竴闃舵鏄剧ず鎻愮ず鏂囨湰 -->
+            <span v-else class="no-promotion-text">棣栬疆姣旇禌</span>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 鍒嗛〉 -->
+      <div class="pagination-container">
+        <el-pagination
+          v-model:current-page="pagination.page"
+          v-model:page-size="pagination.size"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="pagination.total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+
+    <!-- 鏅嬬骇浜哄憳閫夋嫨瀵硅瘽妗� -->
+    <el-dialog
+      v-model="promotionDialogVisible"
+      title="閫夋嫨鏅嬬骇浜哄憳"
+      width="80%"
+      :before-close="handlePromotionDialogClose"
+    >
+      <div v-if="selectedCompetition">
+        <div class="dialog-header">
+          <h4>{{ selectedCompetition.competitionName }} - {{ selectedCompetition.stageName }}</h4>
+          <div class="promotion-info">
+            <p>浠� <strong>{{ promotableData.previousStageName }}</strong> 鏅嬬骇鍒� <strong>{{ promotableData.currentStageName }}</strong></p>
+            <p>涓婁竴闃舵鎬讳汉鏁帮細{{ promotableData.totalCount }}浜猴紝鍙�夋嫨鏅嬬骇锛歿{ promotableData.selectableCount }}浜�</p>
+          </div>
+        </div>
+        
+        <!-- 鎼滅储妗� -->
+        <div class="search-container">
+          <el-input
+            v-model="searchKeyword"
+            placeholder="鎼滅储椤圭洰鍚嶇О鎴栧弬璧涗汉鍛樺鍚�..."
+            clearable
+            style="width: 300px; margin-bottom: 15px"
+            @input="handleSearch"
+          >
+            <template #prefix>
+              <el-icon><Search /></el-icon>
+            </template>
+          </el-input>
+        </div>
+        
+        <!-- 鍙傝禌浜哄憳鍒楄〃 -->
+        <div class="table-info">
+          <el-alert
+            title="鎻愮ず"
+            :description="`鍙傝禌鑰呭凡鎸夊钩鍧囧垎浠庨珮鍒颁綆鎺掑簭锛屽缓璁�夋嫨鍓� ${promotableData.selectableCount} 鍚嶈繘琛屾檵绾"
+            type="info"
+            show-icon
+            :closable="false"
+            style="margin-bottom: 15px"
+          />
+        </div>
+        
+        <el-table
+          :data="participants"
+          v-loading="participantsLoading"
+          @selection-change="handleSelectionChange"
+          style="width: 100%"
+          :row-class-name="getRowClassName"
+        >
+          <el-table-column type="selection" width="55" />
+          <el-table-column type="index" label="鎺掑悕" width="60" align="center" />
+          <el-table-column prop="playerName" label="鍙傝禌鑰呭鍚�" min-width="120" />
+          <el-table-column prop="projectName" label="椤圭洰鍚嶇О" min-width="150" />
+          <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" width="120" />
+          <el-table-column prop="averageScore" label="骞冲潎鍒�" width="100" align="center" sortable>
+            <template #default="scope">
+              <span v-if="scope.row.averageScore > 0" class="score">
+                {{ scope.row.averageScore.toFixed(1) }}
+              </span>
+              <span v-else class="no-score">鏈瘎鍒�</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="ratingCount" label="璇勫娆℃暟" width="100" align="center">
+            <template #default="scope">
+              <el-tag :type="scope.row.ratingCount > 0 ? 'success' : 'info'">
+                {{ scope.row.ratingCount }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="applyTime" label="鎶ュ悕鏃堕棿" width="180">
+            <template #default="scope">
+              {{ formatDate(scope.row.applyTime) }}
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      
+      <template #footer>
+        <div class="dialog-footer">
+          <span class="selected-info">宸查�夋嫨 {{ selectedParticipants.length }} 浜�</span>
+          <el-button @click="handlePromotionDialogClose">鍙栨秷</el-button>
+          <el-button 
+            type="primary" 
+            @click="confirmPromotion"
+            :disabled="selectedParticipants.length === 0"
+            :loading="promotionLoading"
+          >
+            纭鏅嬬骇
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Search } from '@element-plus/icons-vue'
+import PromotionApi from '@/api/promotion'
+
+const router = useRouter()
+
+// 鍝嶅簲寮忔暟鎹�
+const loading = ref(false)
+const participantsLoading = ref(false)
+const promotionLoading = ref(false)
+const promotionDialogVisible = ref(false)
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+  name: ''
+})
+
+// 姣旇禌鏁版嵁
+const competitions = ref([])
+
+// 鍒嗛〉鏁版嵁
+const pagination = reactive({
+  page: 1,
+  size: 10,
+  total: 0
+})
+
+// 閫変腑鐨勬瘮璧�
+const selectedCompetition = ref(null)
+
+// 鍙傝禌浜哄憳鏁版嵁
+const participants = ref([])
+const originalParticipants = ref([]) // 淇濆瓨鍘熷鍙傝禌鑰呮暟鎹敤浜庢悳绱�
+const selectedParticipants = ref([])
+
+// 鎼滅储鍏抽敭璇�
+const searchKeyword = ref('')
+
+// 鍙檵绾у弬璧涜�呮暟鎹�
+const promotableData = ref({
+  participants: [],
+  selectableCount: 0,
+  totalCount: 0,
+  previousStageName: '',
+  currentStageName: ''
+})
+
+// 鍔犺浇姣旇禌鏁版嵁
+const loadData = async () => {
+  loading.value = true
+  try {
+    // 灏濊瘯浣跨敤鐪熷疄API
+    const data = await PromotionApi.getPromotionCompetitions({
+      name: searchForm.name,
+      page: pagination.page,
+      size: pagination.size
+    })
+    
+    competitions.value = data
+    pagination.total = data.length
+  } catch (error) {
+    console.warn('API璋冪敤澶辫触锛屼娇鐢ㄦā鎷熸暟鎹�:', error)
+    
+    // API澶辫触鏃朵娇鐢ㄦā鎷熸暟鎹�
+    const mockData = [
+      {
+        id: 1,
+        competitionName: '2027鍒涙柊鍒涗笟澶ц禌',
+        stageName: '娴烽��',
+        maxParticipants: null,
+        currentCount: 15,
+        status: 1,
+        startTime: '2024-01-01T00:00:00',
+        endTime: '2024-03-31T23:59:59'
+      },
+      {
+        id: 2,
+        competitionName: '2027鍒涙柊鍒涗笟澶ц禌',
+        stageName: '鍒濊禌',
+        maxParticipants: 50,
+        currentCount: 8,
+        status: 1,
+        startTime: '2024-04-01T00:00:00',
+        endTime: '2024-06-30T23:59:59'
+      },
+      {
+        id: 3,
+        competitionName: '2027鍒涙柊鍒涗笟澶ц禌',
+        stageName: '鍐宠禌',
+        maxParticipants: 10,
+        currentCount: 0,
+        status: 0,
+        startTime: '2024-07-01T00:00:00',
+        endTime: '2024-08-31T23:59:59'
+      }
+    ]
+    
+    let filteredData = mockData
+    if (searchForm.name) {
+      filteredData = mockData.filter(item => 
+        item.competitionName.includes(searchForm.name) || 
+        item.stageName.includes(searchForm.name)
+      )
+    }
+    
+    competitions.value = filteredData
+    pagination.total = filteredData.length
+  } finally {
+    loading.value = false
+  }
+}
+
+// 閲嶇疆鎼滅储
+const resetSearch = () => {
+  searchForm.name = ''
+  pagination.page = 1
+  loadData()
+}
+
+// 鍒嗛〉澶勭悊
+const handleSizeChange = (size) => {
+  pagination.size = size
+  pagination.page = 1
+  loadData()
+}
+
+const handleCurrentChange = (page) => {
+  pagination.page = page
+  loadData()
+}
+
+// 鏌ョ湅鍙傝禌浜哄憳
+const viewParticipants = (competition) => {
+  router.push({
+    path: '/project-review',
+    query: {
+      competitionId: competition.id,
+      competitionName: competition.competitionName,
+      stageName: competition.stageName
+    }
+  })
+}
+
+// 閫夋嫨鏅嬬骇浜哄憳
+const selectPromotionCandidates = async (competition) => {
+  selectedCompetition.value = competition
+  promotionDialogVisible.value = true
+  await loadPromotableParticipants(competition.id)
+}
+
+// 鍔犺浇鍙檵绾у弬璧涗汉鍛�
+const loadPromotableParticipants = async (currentStageId) => {
+  participantsLoading.value = true
+  try {
+    // 浣跨敤鏂扮殑API鑾峰彇鍙檵绾у弬璧涜��
+    const data = await PromotionApi.getPromotableParticipants(currentStageId)
+    promotableData.value = data
+    originalParticipants.value = data.participants
+    participants.value = data.participants
+  } catch (error) {
+    console.warn('鑾峰彇鍙檵绾у弬璧涜�匒PI澶辫触锛屼娇鐢ㄦā鎷熸暟鎹�:', error)
+    // API澶辫触鏃朵娇鐢ㄦā鎷熸暟鎹�
+    const mockData = {
+      participants: [
+        {
+          id: 1,
+          playerId: 101,
+          playerName: 'UK2025',
+          projectName: '鏅鸿兘瀹跺眳绯荤粺',
+          phone: '13800138001',
+          averageScore: 85.5,
+          ratingCount: 3,
+          applyTime: '2024-01-15T10:30:00',
+          state: 1
+        },
+        {
+          id: 2,
+          playerId: 102,
+          playerName: '寮犱笁',
+          projectName: 'AI鍥惧儚璇嗗埆',
+          phone: '13800138002',
+          averageScore: 92.0,
+          ratingCount: 3,
+          applyTime: '2024-01-16T14:20:00',
+          state: 1
+        },
+        {
+          id: 3,
+          playerId: 103,
+          playerName: '鏉庡洓',
+          projectName: '鍖哄潡閾惧簲鐢�',
+          phone: '13800138003',
+          averageScore: 78.3,
+          ratingCount: 2,
+          applyTime: '2024-01-17T09:15:00',
+          state: 1
+        }
+      ],
+      selectableCount: 10,
+      totalCount: 15,
+      previousStageName: '娴烽��',
+      currentStageName: '鍒濊禌'
+    }
+    
+    promotableData.value = mockData
+    originalParticipants.value = mockData.participants
+    participants.value = mockData.participants
+  } finally {
+    participantsLoading.value = false
+  }
+}
+
+// 鍔犺浇鍙傝禌浜哄憳锛堜繚鐣欏師鏂规硶浠ュ吋瀹瑰叾浠栧姛鑳斤級
+const loadParticipants = async (competitionId) => {
+  participantsLoading.value = true
+  try {
+    // 灏濊瘯浣跨敤鐪熷疄API鑾峰彇鍙傝禌浜哄憳
+    const participantsData = await PromotionApi.getCompetitionParticipants(competitionId)
+    participants.value = participantsData
+  } catch (error) {
+    console.warn('鑾峰彇鍙傝禌浜哄憳API澶辫触锛屼娇鐢ㄦā鎷熸暟鎹�:', error)
+    // API澶辫触鏃朵娇鐢ㄦā鎷熸暟鎹�
+    const mockParticipants = [
+      {
+        id: 1,
+        playerName: 'UK2025',
+        projectName: '鏅鸿兘瀹跺眳绯荤粺',
+        phone: '13800138001',
+        averageScore: 85.5,
+        ratingCount: 3,
+        applyTime: '2024-01-15T10:30:00'
+      },
+      {
+        id: 2,
+        playerName: '寮犱笁',
+        projectName: 'AI鍥惧儚璇嗗埆',
+        phone: '13800138002',
+        averageScore: 92.0,
+        ratingCount: 3,
+        applyTime: '2024-01-16T14:20:00'
+      },
+      {
+        id: 3,
+        playerName: '鏉庡洓',
+        projectName: '鍖哄潡閾惧簲鐢�',
+        phone: '13800138003',
+        averageScore: 78.3,
+        ratingCount: 2,
+        applyTime: '2024-01-17T09:15:00'
+      }
+    ]
+    
+    participants.value = mockParticipants
+  } finally {
+    participantsLoading.value = false
+  }
+}
+
+// 澶勭悊閫夋嫨鍙樺寲
+const handleSelectionChange = (selection) => {
+  selectedParticipants.value = selection
+}
+
+// 纭鏅嬬骇
+const confirmPromotion = async () => {
+  if (selectedParticipants.value.length === 0) {
+    ElMessage.warning('璇烽�夋嫨瑕佹檵绾х殑浜哄憳')
+    return
+  }
+
+  try {
+    await ElMessageBox.confirm(
+      `纭畾瑕佸皢閫変腑鐨� ${selectedParticipants.value.length} 鍚嶅弬璧涜�呮檵绾у埌涓嬩竴闃舵鍚楋紵`,
+      '纭鏅嬬骇',
+      {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }
+    )
+
+    promotionLoading.value = true
+    
+    try {
+      // 灏濊瘯浣跨敤鐪熷疄API鎵ц鏅嬬骇
+      const participantIds = selectedParticipants.value.map(p => p.id)
+      const result = await PromotionApi.promoteParticipants(
+        selectedCompetition.value.id,
+        participantIds,
+        null // 鐩爣闃舵ID锛岃繖閲屽彲浠ユ牴鎹渶瑕佽缃�
+      )
+      
+      ElMessage.success(result.message || `鎴愬姛鏅嬬骇 ${result.promotedCount} 鍚嶄汉鍛榒)
+    } catch (error) {
+      console.warn('鏅嬬骇API澶辫触锛屼娇鐢ㄦā鎷熸搷浣�:', error)
+      // API澶辫触鏃舵ā鎷熸垚鍔�
+      await new Promise(resolve => setTimeout(resolve, 1000))
+      ElMessage.success(`鎴愬姛鏅嬬骇 ${selectedParticipants.value.length} 鍚嶄汉鍛榒)
+    }
+    
+    handlePromotionDialogClose()
+    loadData() // 閲嶆柊鍔犺浇鏁版嵁
+  } catch {
+    // 鐢ㄦ埛鍙栨秷
+  } finally {
+    promotionLoading.value = false
+  }
+}
+
+// 鍏抽棴鏅嬬骇瀵硅瘽妗�
+const handlePromotionDialogClose = () => {
+  promotionDialogVisible.value = false
+  selectedCompetition.value = null
+  participants.value = []
+  originalParticipants.value = []
+  selectedParticipants.value = []
+  searchKeyword.value = '' // 閲嶇疆鎼滅储鍏抽敭璇�
+  promotableData.value = {
+    participants: [],
+    selectableCount: 0,
+    totalCount: 0,
+    previousStageName: '',
+    currentStageName: ''
+  }
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (status) => {
+  switch (status) {
+    case 1: return 'success'  // 杩涜涓�
+    case 0: return 'warning'  // 鏈彂甯�
+    case 2: return 'danger'   // 鍏抽棴
+    default: return 'info'
+  }
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (status) => {
+  switch (status) {
+    case 1: return '杩涜涓�'
+    case 0: return '鏈彂甯�'
+    case 2: return '鍏抽棴'
+    default: return '鏈煡'
+  }
+}
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (dateString) => {
+  if (!dateString) return '-'
+  const date = new Date(dateString)
+  return date.toLocaleString('zh-CN', {
+    year: 'numeric',
+    month: '2-digit',
+    day: '2-digit',
+    hour: '2-digit',
+    minute: '2-digit'
+  })
+}
+
+// 鑾峰彇琛ㄦ牸琛屾牱寮忕被鍚�
+const getRowClassName = ({ rowIndex }) => {
+  // 楂樹寒鎺ㄨ崘鏅嬬骇鐨勪汉鍛橈紙鍓峴electableCount鍚嶏級
+  if (rowIndex < promotableData.value.selectableCount) {
+    return 'recommended-row'
+  }
+  return ''
+}
+
+// 鎼滅储澶勭悊鏂规硶
+const handleSearch = () => {
+  if (!searchKeyword.value.trim()) {
+    // 濡傛灉鎼滅储鍏抽敭璇嶄负绌猴紝鏄剧ず鎵�鏈夊師濮嬫暟鎹�
+    participants.value = originalParticipants.value
+  } else {
+    // 鏍规嵁鍏抽敭璇嶈繃婊ゅ弬璧涜��
+    const keyword = searchKeyword.value.toLowerCase().trim()
+    participants.value = originalParticipants.value.filter(participant => {
+      const projectName = participant.projectName?.toLowerCase() || ''
+      const playerName = participant.playerName?.toLowerCase() || ''
+      
+      return projectName.includes(keyword) || playerName.includes(keyword)
+    })
+  }
+  
+  // 娓呯┖宸查�夋嫨鐨勫弬璧涜�咃紙鍥犱负鍒楄〃宸叉敼鍙橈級
+  selectedParticipants.value = []
+}
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(() => {
+  loadData()
+})
+</script>
+
+<style lang="scss" scoped>
+.promotion-container {
+  padding: 20px;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.card-title {
+  margin: 0;
+  font-size: 18px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.search-form {
+  margin-bottom: 20px;
+  padding: 20px;
+  background: #f8f9fa;
+  border-radius: 8px;
+}
+
+.competition-info {
+  .main-name {
+    font-weight: 600;
+    color: #303133;
+    margin-bottom: 4px;
+  }
+  
+  .stage-name {
+    font-size: 12px;
+    color: #409eff;
+    background: #ecf5ff;
+    padding: 2px 8px;
+    border-radius: 12px;
+    display: inline-block;
+  }
+}
+
+.count-link {
+  font-weight: 600;
+  color: #409eff;
+  
+  &:hover {
+    color: #66b1ff;
+  }
+}
+
+.score {
+  font-weight: 600;
+  color: #67c23a;
+}
+
+.no-score {
+  color: #909399;
+  font-size: 12px;
+}
+
+.no-promotion-text {
+  color: #909399;
+  font-size: 12px;
+  font-style: italic;
+}
+
+.pagination-container {
+  margin-top: 20px;
+  display: flex;
+  justify-content: center;
+}
+
+.dialog-header {
+  margin-bottom: 20px;
+  padding-bottom: 15px;
+  border-bottom: 1px solid #ebeef5;
+  
+  h4 {
+    margin: 0 0 8px 0;
+    color: #303133;
+    font-size: 16px;
+    font-weight: 600;
+  }
+  
+  .promotion-info {
+    p {
+      margin: 4px 0;
+      color: #606266;
+      font-size: 14px;
+      
+      strong {
+        color: #409eff;
+        font-weight: 600;
+      }
+    }
+  }
+}
+
+// 鎺ㄨ崘鏅嬬骇琛屾牱寮�
+:deep(.recommended-row) {
+  background-color: #f0f9ff !important;
+  
+  &:hover {
+    background-color: #e1f5fe !important;
+  }
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  
+  .selected-info {
+    color: #606266;
+    font-size: 14px;
+  }
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/player/detail.vue b/web/src/views/player/detail.vue
new file mode 100644
index 0000000..6843618
--- /dev/null
+++ b/web/src/views/player/detail.vue
@@ -0,0 +1,774 @@
+<template>
+  <div class="player-detail">
+    <div class="page-header">
+      <el-button @click="goBack" type="primary" plain>
+        <el-icon><ArrowLeft /></el-icon>
+        杩斿洖鍒楄〃
+      </el-button>
+      <h2>鎶ュ悕璇︽儏</h2>
+    </div>
+
+    <div v-if="loading" class="loading-container">
+      <el-skeleton :rows="8" animated />
+    </div>
+
+    <div v-else-if="playerData" class="detail-content">
+      <!-- 鍩烘湰淇℃伅鍗$墖 -->
+      <el-card class="info-card" shadow="hover">
+        <template #header>
+          <div class="card-header">
+            <span>鍩烘湰淇℃伅</span>
+          </div>
+        </template>
+        
+        <div class="basic-info">
+          <div class="avatar-section">
+            <el-avatar 
+              :size="120" 
+              :src="playerData.avatarUrl" 
+              :icon="UserFilled"
+              class="player-avatar"
+            />
+          </div>
+          
+          <div class="info-grid">
+            <div class="info-item">
+              <label>濮撳悕锛�</label>
+              <span>{{ playerData.name || '-' }}</span>
+            </div>
+            <div class="info-item">
+              <label>鎬у埆锛�</label>
+              <span>{{ getGenderText(playerData.gender) }}</span>
+            </div>
+            <div class="info-item">
+              <label>鍑虹敓鏃ユ湡锛�</label>
+              <span>{{ formatDate(playerData.birthday) }}</span>
+            </div>
+            <div class="info-item">
+              <label>鎵嬫満鍙凤細</label>
+              <span>{{ playerData.phone || '-' }}</span>
+            </div>
+            <div class="info-item">
+              <label>鍖哄煙锛�</label>
+              <span>{{ playerData.regionName || '-' }}</span>
+            </div>
+            <div class="info-item">
+              <label>瀛﹀巻锛�</label>
+              <span>{{ getEducationText(playerData.education) }}</span>
+            </div>
+          </div>
+        </div>
+        
+        <div v-if="playerData.introduction" class="introduction-section">
+            <h3>涓汉浠嬬粛</h3>
+            <div class="introduction-content">{{ playerData.introduction }}</div>
+          </div>
+      </el-card>
+
+      <!-- 鍙傝禌椤圭洰淇℃伅鍗$墖 -->
+      <el-card class="info-card" shadow="hover">
+        <template #header>
+          <div class="card-header">
+            <span>鍙傝禌椤圭洰淇℃伅</span>
+          </div>
+        </template>
+        
+        <div class="project-info">
+          <div class="info-item">
+            <label>姣旇禌鍚嶇О锛�</label>
+            <span>{{ activityPlayerData.activityName || '-' }}</span>
+          </div>
+          <div class="info-item">
+            <label>鎶ュ悕鏃堕棿锛�</label>
+            <span>{{ formatDateTime(activityPlayerData.createTime) }}</span>
+          </div>
+          <div class="info-item">
+            <label>瀹℃牳鐘舵�侊細</label>
+            <el-tag :type="getStatusType(activityPlayerData.state)">
+              {{ getStatusText(activityPlayerData.state) }}
+            </el-tag>
+          </div>
+        </div>
+        
+        <!-- 椤圭洰鍚嶇О鍗曠嫭涓�琛� -->
+        <div v-if="activityPlayerData.projectName" class="project-name-section">
+          <h4>椤圭洰鍚嶇О</h4>
+          <div class="project-name-content">{{ activityPlayerData.projectName }}</div>
+        </div>
+        
+        <div v-if="activityPlayerData.description" class="description-section">
+          <h4>椤圭洰鎻忚堪</h4>
+          <div class="description-content">{{ activityPlayerData.description }}</div>
+        </div>
+        
+        <!-- 闄勪欢鍒楄〃 -->
+        <div v-if="attachments.length > 0" class="attachments-section">
+          <h4>椤圭洰闄勪欢</h4>
+          <div class="attachments-list">
+            <div 
+              v-for="attachment in attachments" 
+              :key="attachment.id"
+              class="attachment-item"
+            >
+              <el-icon class="attachment-icon"><Document /></el-icon>
+              <span class="attachment-name">{{ attachment.originalName }}</span>
+              <el-button 
+                type="primary" 
+                size="small" 
+                @click="downloadAttachment(attachment)"
+              >
+                涓嬭浇
+              </el-button>
+            </div>
+          </div>
+        </div>
+      </el-card>
+
+      <!-- 瀹℃牳鍔熻兘鍗$墖 -->
+      <el-card class="info-card review-card" shadow="hover">
+        <template #header>
+          <div class="card-header">
+            <span>瀹℃牳绠$悊</span>
+          </div>
+        </template>
+        
+        <div class="review-section">
+          <div class="review-status">
+            <label>褰撳墠鐘舵�侊細</label>
+            <el-tag :type="getStatusType(activityPlayerData.state)" size="large">
+              {{ getStatusText(activityPlayerData.state) }}
+            </el-tag>
+          </div>
+          
+          <div class="feedback-section">
+            <label>瀹℃牳鎰忚锛�</label>
+            <el-input
+              v-model="feedbackText"
+              type="textarea"
+              :rows="4"
+              placeholder="璇疯緭鍏ュ鏍告剰瑙�..."
+              maxlength="500"
+              show-word-limit
+              class="feedback-input"
+            />
+          </div>
+          
+          <div class="review-actions">
+            <el-button 
+              type="success" 
+              @click="handleApprove"
+              :loading="approving"
+              :disabled="activityPlayerData.state === 1"
+            >
+              閫氳繃
+            </el-button>
+            <el-button 
+              type="danger" 
+              @click="handleReject"
+              :loading="rejecting"
+              :disabled="activityPlayerData.state === 2"
+            >
+              椹冲洖
+            </el-button>
+            <el-button 
+              type="primary" 
+              @click="handleUpdateFeedback"
+              :loading="updating"
+            >
+              鏇存柊鎰忚
+            </el-button>
+            <el-button 
+              type="info" 
+              @click="handleClose"
+            >
+              鍏抽棴
+            </el-button>
+          </div>
+          
+          <div v-if="activityPlayerData.feedback" class="current-feedback">
+            <label>褰撳墠瀹℃牳鎰忚锛�</label>
+            <div class="feedback-content">{{ activityPlayerData.feedback }}</div>
+          </div>
+        </div>
+      </el-card>
+    </div>
+
+    <div v-else class="error-container">
+      <el-result
+        icon="warning"
+        title="鏁版嵁鍔犺浇澶辫触"
+        sub-title="鏃犳硶鑾峰彇鍙傝禌浜哄憳璇︽儏淇℃伅"
+      >
+        <template #extra>
+          <el-button type="primary" @click="loadData">閲嶆柊鍔犺浇</el-button>
+        </template>
+      </el-result>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import { graphqlRequest } from '@/config/api'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { ArrowLeft, UserFilled, Document } from '@element-plus/icons-vue'
+import { approveActivityPlayer, rejectActivityPlayer, updatePlayerFeedback } from '@/api/activityPlayer.js'
+
+const route = useRoute()
+const router = useRouter()
+
+// 鍝嶅簲寮忔暟鎹�
+const loading = ref(true)
+const playerData = ref<any>(null)
+const activityPlayerData = ref<any>(null)
+const attachments = ref<any[]>([])
+
+// 瀹℃牳鐩稿叧鏁版嵁
+const feedbackText = ref('')
+const approving = ref(false)
+const rejecting = ref(false)
+const updating = ref(false)
+
+// 椤甸潰鍔犺浇
+onMounted(() => {
+  loadData()
+})
+
+// 鍔犺浇鏁版嵁
+const loadData = async () => {
+  try {
+    loading.value = true
+    const playerId = route.params.id as string
+    
+    // 杩欓噷搴旇璋冪敤API鑾峰彇鏁版嵁
+    // 鏆傛椂浣跨敤妯℃嫙鏁版嵁
+    await loadPlayerData(playerId)
+    
+  } catch (error) {
+    console.error('鍔犺浇鏁版嵁澶辫触:', error)
+    ElMessage.error('鍔犺浇鏁版嵁澶辫触')
+  } finally {
+    loading.value = false
+  }
+}
+
+// GraphQL鏌ヨ
+const ACTIVITY_PLAYER_DETAIL_QUERY = `
+  query ActivityPlayerDetail($id: ID!) {
+    activityPlayerDetail(id: $id) {
+      id
+      playerInfo {
+        id
+        name
+        phone
+        gender
+        birthday
+        education
+        introduction
+        description
+        avatarUrl
+        avatar {
+          id
+          name
+          path
+          fullUrl
+          fullThumbUrl
+          fileSize
+          fileExt
+          mediaType
+        }
+      }
+      regionInfo {
+        id
+        name
+        fullPath
+      }
+      activityName
+      projectName
+      description
+      feedback
+      state
+      submissionFiles {
+        id
+        name
+        url
+        fileExt
+        fileSize
+        mediaType
+      }
+    }
+  }
+`
+
+// 浣跨敤缁熶竴鐨凣raphQL璇锋眰鍑芥暟
+
+// 鍔犺浇鎵�鏈夋暟鎹�
+const loadPlayerData = async (playerId: string) => {
+  try {
+    const data = await graphqlRequest(ACTIVITY_PLAYER_DETAIL_QUERY, { id: playerId })
+    const detail = data.activityPlayerDetail
+    
+    if (detail) {
+      // 璁剧疆player鍩烘湰淇℃伅
+      playerData.value = {
+        id: detail.playerInfo.id,
+        name: detail.playerInfo.name,
+        phone: detail.playerInfo.phone,
+        gender: detail.playerInfo.gender,
+        birthday: detail.playerInfo.birthday,
+        education: detail.playerInfo.education,
+        introduction: detail.playerInfo.introduction,
+        description: detail.playerInfo.description,
+        avatarUrl: detail.playerInfo.avatar?.fullUrl || detail.playerInfo.avatarUrl,
+        regionName: detail.regionInfo?.fullPath || detail.regionInfo?.name || '-'
+      }
+      
+      // 璁剧疆activity_player鏁版嵁
+      activityPlayerData.value = {
+        id: detail.id,
+        activityName: detail.activityName,
+        projectName: detail.projectName || '-',
+        description: detail.description,
+        feedback: detail.feedback || '',
+        state: detail.state || 0,
+        createTime: new Date().toISOString()
+      }
+      
+      // 璁剧疆闄勪欢鏁版嵁
+      attachments.value = detail.submissionFiles.map((file: any) => ({
+        id: file.id,
+        originalName: file.name,
+        url: file.url,
+        fileSize: file.fileSize ? `${(file.fileSize / 1024 / 1024).toFixed(1)}MB` : '-'
+      }))
+      
+      // 鍒濆鍖栧鏍告剰瑙�
+      feedbackText.value = detail.feedback || ''
+    }
+  } catch (error) {
+    console.error('鍔犺浇鏁版嵁澶辫触:', error)
+    throw error
+  }
+}
+
+// 绉婚櫎鍗曠嫭鐨勫姞杞藉嚱鏁帮紝缁熶竴鍦╨oadPlayerData涓鐞�
+const loadActivityPlayerData = async (playerId: string) => {
+  // 宸插湪loadPlayerData涓鐞�
+}
+
+const loadAttachments = async (playerId: string) => {
+  // 宸插湪loadPlayerData涓鐞�
+}
+
+// 杩斿洖鍒楄〃
+const goBack = () => {
+  router.push('/player')
+}
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (dateStr: string) => {
+  if (!dateStr) return '-'
+  return new Date(dateStr).toLocaleDateString('zh-CN')
+}
+
+// 鏍煎紡鍖栨棩鏈熸椂闂�
+const formatDateTime = (dateStr: string) => {
+  if (!dateStr) return '-'
+  return new Date(dateStr).toLocaleString('zh-CN')
+}
+
+// 鑾峰彇鎬у埆鏂囨湰
+const getGenderText = (gender: number) => {
+  const genderMap: Record<number, string> = {
+    1: '鐢�',
+    2: '濂�'
+  }
+  return genderMap[gender] || '-'
+}
+
+// 鑾峰彇瀛﹀巻鏂囨湰
+const getEducationText = (education: number) => {
+  const educationMap: Record<number, string> = {
+    1: '楂樹腑',
+    2: '澶т笓',
+    3: '鏈',
+    4: '纭曞+',
+    5: '鍗氬+'
+  }
+  return educationMap[education] || '-'
+}
+
+// 鑾峰彇鐘舵�佹枃鏈�
+const getStatusText = (state: number) => {
+  const statusMap: Record<number, string> = {
+    0: '鏈鏍�',
+    1: '瀹℃牳閫氳繃',
+    2: '瀹℃牳椹冲洖'
+  }
+  return statusMap[state] || '-'
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStatusType = (state: number) => {
+  const typeMap: Record<number, string> = {
+    0: 'warning',
+    1: 'success',
+    2: 'danger'
+  }
+  return typeMap[state] || 'info'
+}
+
+// 涓嬭浇闄勪欢
+const downloadAttachment = (attachment: any) => {
+  // TODO: 瀹炵幇闄勪欢涓嬭浇鍔熻兘
+  window.open(attachment.url, '_blank')
+}
+
+// 瀹℃牳閫氳繃
+const handleApprove = async () => {
+  try {
+    await ElMessageBox.confirm('纭瀹℃牳閫氳繃璇ユ姤鍚嶇敵璇凤紵', '纭鎿嶄綔', {
+      confirmButtonText: '纭',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    })
+    
+    approving.value = true
+    const result = await approveActivityPlayer(activityPlayerData.value.id, feedbackText.value)
+    
+    if (result.approveActivityPlayer) {
+      ElMessage.success('瀹℃牳閫氳繃鎴愬姛')
+      activityPlayerData.value.state = 1
+      activityPlayerData.value.feedback = feedbackText.value
+    } else {
+      ElMessage.error('瀹℃牳閫氳繃澶辫触')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('瀹℃牳閫氳繃澶辫触:', error)
+      ElMessage.error('瀹℃牳閫氳繃澶辫触')
+    }
+  } finally {
+    approving.value = false
+  }
+}
+
+// 瀹℃牳椹冲洖
+const handleReject = async () => {
+  if (!feedbackText.value.trim()) {
+    ElMessage.warning('椹冲洖鏃跺繀椤诲~鍐欏鏍告剰瑙�')
+    return
+  }
+  
+  try {
+    await ElMessageBox.confirm('纭椹冲洖璇ユ姤鍚嶇敵璇凤紵', '纭鎿嶄綔', {
+      confirmButtonText: '纭',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    })
+    
+    rejecting.value = true
+    const result = await rejectActivityPlayer(activityPlayerData.value.id, feedbackText.value)
+    
+    if (result.rejectActivityPlayer) {
+      ElMessage.success('瀹℃牳椹冲洖鎴愬姛')
+      activityPlayerData.value.state = 2
+      activityPlayerData.value.feedback = feedbackText.value
+    } else {
+      ElMessage.error('瀹℃牳椹冲洖澶辫触')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('瀹℃牳椹冲洖澶辫触:', error)
+      ElMessage.error('瀹℃牳椹冲洖澶辫触')
+    }
+  } finally {
+    rejecting.value = false
+  }
+}
+
+// 鏇存柊瀹℃牳鎰忚
+const handleUpdateFeedback = async () => {
+  if (!feedbackText.value.trim()) {
+    ElMessage.warning('璇峰~鍐欏鏍告剰瑙�')
+    return
+  }
+  
+  try {
+    updating.value = true
+    const result = await updatePlayerFeedback(activityPlayerData.value.id, feedbackText.value)
+    
+    if (result.updatePlayerFeedback) {
+      ElMessage.success('瀹℃牳鎰忚鏇存柊鎴愬姛')
+      activityPlayerData.value.feedback = feedbackText.value
+    } else {
+      ElMessage.error('瀹℃牳鎰忚鏇存柊澶辫触')
+    }
+  } catch (error) {
+    console.error('鏇存柊瀹℃牳鎰忚澶辫触:', error)
+    ElMessage.error('鏇存柊瀹℃牳鎰忚澶辫触')
+  } finally {
+    updating.value = false
+  }
+}
+
+// 鍏抽棴椤甸潰
+const handleClose = () => {
+  goBack()
+}
+</script>
+
+<style scoped lang="scss">
+.player-detail {
+  padding: 20px;
+  
+  .page-header {
+    display: flex;
+    align-items: center;
+    gap: 16px;
+    margin-bottom: 24px;
+    
+    h2 {
+      margin: 0;
+      color: #303133;
+    }
+  }
+  
+  .loading-container {
+    padding: 40px;
+  }
+  
+  .detail-content {
+    display: flex;
+    flex-direction: column;
+    gap: 24px;
+  }
+  
+  .info-card {
+    .card-header {
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+  
+  .basic-info {
+    display: flex;
+    gap: 32px;
+    margin-bottom: 24px;
+    
+    .avatar-section {
+      flex-shrink: 0;
+      
+      .player-avatar {
+        border: 2px solid #e4e7ed;
+      }
+    }
+    
+    .info-grid {
+      flex: 1;
+      display: grid;
+      grid-template-columns: repeat(2, 1fr);
+      gap: 16px;
+      
+      .info-item {
+        display: flex;
+        align-items: center;
+        
+        label {
+          font-weight: 500;
+          color: #606266;
+          min-width: 80px;
+        }
+        
+        span {
+          color: #303133;
+        }
+      }
+    }
+  }
+  
+  .introduction-section, .description-section {
+    margin-top: 24px;
+    
+    h4 {
+      margin: 0 0 12px 0;
+      color: #303133;
+      font-weight: 600;
+    }
+    
+    .introduction-content, .description-content {
+      padding: 16px;
+      background-color: #f8f9fa;
+      border-radius: 6px;
+      line-height: 1.6;
+      color: #606266;
+    }
+  }
+  
+  .project-info {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 16px;
+    
+    .info-item {
+      display: flex;
+      align-items: center;
+      
+      label {
+        font-weight: 500;
+        color: #606266;
+        min-width: 100px;
+      }
+      
+      span {
+        color: #303133;
+      }
+    }
+  }
+  
+  .project-name-section {
+    margin-top: 24px;
+    
+    h4 {
+      margin: 0 0 12px 0;
+      color: #303133;
+      font-weight: 600;
+    }
+    
+    .project-name-content {
+      padding: 12px;
+      background-color: #f8f9fa;
+      border-radius: 6px;
+      border-left: 4px solid #409eff;
+      color: #303133;
+      font-weight: 500;
+    }
+  }
+  
+  .attachments-section {
+    margin-top: 24px;
+    
+    h4 {
+      margin: 0 0 16px 0;
+      color: #303133;
+      font-weight: 600;
+    }
+    
+    .attachments-list {
+      display: flex;
+      flex-direction: column;
+      gap: 12px;
+      
+      .attachment-item {
+        display: flex;
+        align-items: center;
+        gap: 12px;
+        padding: 12px;
+        background-color: #f8f9fa;
+        border-radius: 6px;
+        border: 1px solid #e4e7ed;
+        
+        .attachment-icon {
+          color: #409eff;
+          font-size: 20px;
+        }
+        
+        .attachment-name {
+          flex: 1;
+          color: #303133;
+        }
+      }
+    }
+  }
+  
+  .error-container {
+    padding: 40px;
+    text-align: center;
+  }
+  
+  .review-card {
+    margin-top: 24px;
+    
+    .review-status {
+      margin-bottom: 16px;
+      
+      .status-label {
+        font-weight: 600;
+        color: #303133;
+        margin-right: 8px;
+      }
+    }
+    
+    .feedback-section {
+      margin-bottom: 16px;
+      
+      .feedback-label {
+        display: block;
+        margin-bottom: 8px;
+        font-weight: 600;
+        color: #303133;
+      }
+      
+      .feedback-textarea {
+        width: 100%;
+        min-height: 100px;
+        resize: vertical;
+      }
+      
+      .char-count {
+        text-align: right;
+        margin-top: 4px;
+        font-size: 12px;
+        color: #909399;
+      }
+    }
+    
+    .review-actions {
+      display: flex;
+      gap: 12px;
+      flex-wrap: wrap;
+      
+      .el-button {
+        min-width: 80px;
+      }
+    }
+    
+    .current-feedback {
+      margin-top: 16px;
+      padding: 12px;
+      background-color: #f8f9fa;
+      border-radius: 6px;
+      border-left: 4px solid #409eff;
+      
+      .feedback-label {
+        font-weight: 600;
+        color: #303133;
+        margin-bottom: 8px;
+      }
+      
+      .feedback-content {
+        color: #606266;
+        line-height: 1.6;
+        white-space: pre-wrap;
+      }
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .basic-info {
+    flex-direction: column;
+    align-items: center;
+    text-align: center;
+    
+    .info-grid {
+      grid-template-columns: 1fr;
+    }
+  }
+  
+  .project-info {
+    grid-template-columns: 1fr;
+  }
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/player/index.vue b/web/src/views/player/index.vue
index 71f6665..3531e92 100644
--- a/web/src/views/player/index.vue
+++ b/web/src/views/player/index.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="player-page">
     <div class="page-card">
-      <h3 class="card-title">姣旇禌鎶ュ悕</h3>
+      <h3 class="card-title">鎶ュ悕瀹℃牳</h3>
       
       <!-- 鎼滅储鍜屾搷浣滄爮 -->
       <div class="toolbar">
@@ -26,13 +26,10 @@
           <el-option 
             v-for="activity in activityOptions" 
             :key="activity.id" 
-            :label="getActivityDisplayName(activity)" 
+            :label="activity.name" 
             :value="activity.id"
           >
-            <span>{{ getActivityName(activity) }}</span>
-            <span v-if="activity.pid > 0" style="color: #409eff; margin-left: 8px;">
-              {{ activity.name }}
-            </span>
+            {{ activity.name }}
           </el-option>
         </el-select>
         <el-select
@@ -41,10 +38,9 @@
           style="width: 150px"
           clearable
         >
-          <el-option label="鏈鏍�" value="0" />
-          <el-option label="杩涜涓�" value="1" />
-          <el-option label="宸查┏鍥�" value="2" />
-          <el-option label="宸茬粨鏉�" value="3" />
+          <el-option label="寰呭鏍�" value="0" />
+          <el-option label="閫氳繃" value="1" />
+          <el-option label="椹冲洖" value="2" />
         </el-select>
         <el-button type="primary" @click="handleSearch">
           <el-icon><Search /></el-icon>
@@ -70,27 +66,12 @@
             <el-tag :type="getStateType(row.state)">{{ getStateText(row.state) }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="鎿嶄綔" width="200" fixed="right">
+        <el-table-column label="鎿嶄綔" width="120" fixed="right">
           <template #default="{ row }">
             <div class="table-actions">
-              <el-button 
-                v-if="row.state === 1" 
-                type="success" 
-                size="small" 
-                @click="handleApprove(row)"
-              >
-                瀹℃牳閫氳繃
-              </el-button>
-              <el-button 
-                v-if="row.state === 1" 
-                type="danger" 
-                size="small" 
-                @click="handleReject(row)"
-              >
-                瀹℃牳鎷掔粷
-              </el-button>
-              <el-button type="primary" size="small" @click="handleView(row)">
-                璇勫垎璇︽儏
+              <!-- 鍙繚鐣欒鎯呮寜閽� -->
+              <el-button type="primary" size="small" @click="handleViewDetail(row)">
+                璇︽儏
               </el-button>
             </div>
           </template>
@@ -149,7 +130,7 @@
     activityName: '2024骞村垱鏂板垱涓氬ぇ璧�',
     phone: '13800138001',
     applyTime: '2024-01-05 14:30:00',
-    state: 1 // 1-寰呭鏍�, 2-杩涜涓�, 3-宸茬粨鏉�
+    state: 0 // 0-鏈鏍�
   },
   {
     id: 2,
@@ -158,7 +139,7 @@
     activityName: '涔︽硶姣旇禌',
     phone: '13900139002',
     applyTime: '2024-01-16 10:30:00',
-    state: 2
+    state: 1 // 1-瀹℃牳閫氳繃
   },
   {
     id: 3,
@@ -167,16 +148,25 @@
     activityName: '缁樼敾姣旇禌',
     phone: '13900139003',
     applyTime: '2024-01-17 14:20:00',
-    state: 1
+    state: 2 // 2-瀹℃牳椹冲洖
+  },
+  {
+    id: 4,
+    name: '璧靛叚',
+    avatar: '',
+    activityName: '闊充箰姣旇禌',
+    phone: '13900139004',
+    applyTime: '2024-01-18 09:15:00',
+    state: 0 // 0-鏈鏍�
   }
 ])
 
 // 鑾峰彇鐘舵�佹爣绛剧被鍨�
 const getStateType = (state: number | null | undefined) => {
   const typeMap: Record<number, string> = {
-    0: 'warning',   // 寰呭鏍�
-    1: 'success',   // 杩涜涓�
-    2: 'danger',    // 鏈�氳繃
+    0: 'warning',   // 鏈鏍�
+    1: 'success',   // 瀹℃牳閫氳繃
+    2: 'danger',    // 瀹℃牳椹冲洖
     3: 'info'       // 宸茬粨鏉�
   }
   return state != null ? (typeMap[state] || 'info') : 'info'
@@ -185,9 +175,9 @@
 // 鑾峰彇鐘舵�佹枃鏈�
 const getStateText = (state: number | null | undefined) => {
   const textMap: Record<number, string> = {
-    0: '寰呭鏍�',
-    1: '杩涜涓�',
-    2: '鏈�氳繃',
+    0: '鏈鏍�',
+    1: '瀹℃牳閫氳繃',
+    2: '瀹℃牳椹冲洖',
     3: '宸茬粨鏉�'
   }
   return state != null ? (textMap[state] || '鏈煡') : '鏈煡'
@@ -199,35 +189,16 @@
   loadData()
 }
 
-// 瀹℃牳閫氳繃
-const handleApprove = async (row: any) => {
-  try {
-    await ElMessageBox.confirm(`纭畾瀹℃牳閫氳繃瀛﹀憳"${row.name}"鐨勬姤鍚嶇敵璇峰悧锛焋, '鎻愮ず', {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: 'success'
-    })
-    
-    ElMessage.success('瀹℃牳閫氳繃鎴愬姛')
-    row.state = 2
-  } catch {
-    // 鐢ㄦ埛鍙栨秷
-  }
-}
 
-// 瀹℃牳鎷掔粷
-const handleReject = (row: any) => {
-  ElMessageBox.confirm('纭鎷掔粷璇ョ敵璇凤紵', '鎻愮ず', {
-    confirmButtonText: '纭畾',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  }).then(() => {
-    // 杩欓噷搴旇璋冪敤API鏇存柊鐘舵��
-    row.state = 3 // 鏇存柊涓哄凡缁撴潫
-    ElMessage.success('宸叉嫆缁�')
-  }).catch(() => {
-    ElMessage.info('宸插彇娑�')
-  })
+
+// 鏌ョ湅璇︽儏锛堣烦杞埌璇︽儏椤甸潰锛屽彧璇绘ā寮忥級
+const handleViewDetail = (row: any) => {
+  if (!row.id) {
+    ElMessage.error('鏃犳硶鑾峰彇鎶ュ悕璁板綍ID')
+    return
+  }
+  // 璺宠浆鍒拌鎯呴〉闈紙鍙妯″紡锛�
+  router.push(`/player/${row.id}/detail`)
 }
 
 // 鏌ョ湅璇︽儏锛堣烦杞埌璇勫垎椤甸潰锛�
@@ -259,13 +230,7 @@
   return activity.name
 }
 
-// 鑾峰彇娲诲姩鏄剧ず鍚嶇О锛堢敤浜庢悳绱㈠拰閫変腑鏃舵樉绀猴級
-const getActivityDisplayName = (activity: any) => {
-  if (activity.pid > 0 && activity.parent) {
-    return `${activity.parent.name} - ${activity.name}`
-  }
-  return activity.name
-}
+
 
 // 鍔犺浇娲诲姩閫夐」
 const loadActivityOptions = async () => {
@@ -285,6 +250,7 @@
     const list = await PlayerApi.getApplications(
       searchForm.name || '', 
       searchForm.activityId || null, 
+      searchForm.state !== '' ? parseInt(searchForm.state) : null,
       pagination.page, 
       pagination.size
     )
@@ -331,8 +297,11 @@
     display: flex;
     gap: 8px;
     flex-wrap: wrap;
+    align-items: center;
   }
   
+
+  
   .pagination {
     margin-top: 20px;
     display: flex;
diff --git a/web/src/views/project-review/detail.vue b/web/src/views/project-review/detail.vue
new file mode 100644
index 0000000..f59bbe1
--- /dev/null
+++ b/web/src/views/project-review/detail.vue
@@ -0,0 +1,530 @@
+<template>
+  <div class="detail-container">
+    <el-card v-loading="loading">
+      <template #header>
+        <div class="card-header">
+          <h3 class="card-title">椤圭洰璇勫璇︽儏</h3>
+          <el-button @click="goBack">杩斿洖</el-button>
+        </div>
+      </template>
+
+      <el-row :gutter="20" v-if="projectDetail">
+        <!-- 宸︿晶锛氶」鐩俊鎭� -->
+        <el-col :span="16">
+          <div class="project-section">
+            <!-- 椤圭洰鍩烘湰淇℃伅 -->
+            <h4>椤圭洰淇℃伅</h4>
+            <el-descriptions :column="2" border>
+              <el-descriptions-item label="椤圭洰鍚嶇О">
+                {{ projectDetail.projectName || '鏈~鍐�' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="姣旇禌鍚嶇О">
+                {{ projectDetail.activityName }}
+              </el-descriptions-item>
+              <el-descriptions-item label="椤圭洰鎻忚堪" :span="2">
+                <div class="description-content">
+                  {{ projectDetail.description || '鏆傛棤鎻忚堪' }}
+                </div>
+              </el-descriptions-item>
+            </el-descriptions>
+
+            <!-- 椤圭洰闄勪欢 -->
+            <h4 style="margin-top: 20px;">椤圭洰闄勪欢</h4>
+            <div class="attachments" v-if="projectDetail.submissionFiles && projectDetail.submissionFiles.length > 0">
+              <div v-for="file in projectDetail.submissionFiles" :key="file.id" class="attachment-item">
+                <div class="file-info">
+                  <el-icon class="file-icon"><Document /></el-icon>
+                  <span class="file-name">{{ file.name }}</span>
+                  <span class="file-size">{{ formatFileSize(file.fileSize) }}</span>
+                </div>
+                <div class="file-actions">
+                  <el-button type="primary" link @click="previewFile(file)">
+                    棰勮
+                  </el-button>
+                  <el-button type="primary" link @click="downloadFile(file)">
+                    涓嬭浇
+                  </el-button>
+                </div>
+              </div>
+            </div>
+            <div v-else class="no-attachments">
+              <el-empty description="鏆傛棤闄勪欢" />
+            </div>
+
+            <!-- 鍙傝禌浜轰俊鎭� -->
+            <h4 style="margin-top: 20px;">鍙傝禌浜轰俊鎭�</h4>
+            <el-descriptions :column="2" border v-if="projectDetail.playerInfo">
+              <el-descriptions-item label="澶村儚">
+                <el-avatar 
+                  :src="projectDetail.playerInfo.avatarUrl" 
+                  :size="60"
+                  :icon="UserFilled"
+                />
+              </el-descriptions-item>
+              <el-descriptions-item label="濮撳悕">
+                {{ projectDetail.playerInfo.name }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鑱旂郴鐢佃瘽">
+                {{ projectDetail.playerInfo.phone }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鎵�灞炲尯鍩�" v-if="projectDetail.regionInfo">
+                {{ projectDetail.regionInfo.name }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鎶ュ悕鐘舵��">
+                <el-tag :type="getStateType(projectDetail.state)">
+                  {{ getStateName(projectDetail.state) }}
+                </el-tag>
+              </el-descriptions-item>
+            </el-descriptions>
+          </div>
+        </el-col>
+
+        <!-- 鍙充晶锛氳瘎鍒嗕俊鎭� -->
+        <el-col :span="8">
+          <div class="rating-section">
+            <!-- 璇勫缁熻 -->
+            <h4>璇勫淇℃伅</h4>
+            <el-card class="rating-summary">
+              <div class="rating-item">
+                <span class="label">宸茶瘎瀹℃鏁帮細</span>
+                <span class="value">{{ ratingStats.ratingCount }}</span>
+              </div>
+              <div class="rating-item">
+                <span class="label">褰撳墠骞冲潎鍒嗭細</span>
+                <span class="value score">
+                  {{ ratingStats.averageScore > 0 ? ratingStats.averageScore.toFixed(1) : '鏈瘎鍒�' }}
+                </span>
+              </div>
+            </el-card>
+
+            <!-- 璇勫垎妯℃澘 -->
+            <h4 style="margin-top: 20px;">璇勫垎妯℃澘</h4>
+            <div class="rating-template" v-if="projectDetail.ratingForm">
+              <div class="template-header">
+                <span class="template-name">{{ projectDetail.ratingForm.schemeName }}</span>
+                <span class="template-total">鎬诲垎锛歿{ projectDetail.ratingForm.totalMaxScore }}鍒�</span>
+              </div>
+              
+              <div v-for="item in ratingItems" :key="item.id" class="template-item">
+                <div class="item-header">
+                  <span class="item-name">{{ item.name }}</span>
+                  <span class="item-score">{{ item.maxScore }}鍒�</span>
+                </div>
+                <el-input-number
+                  v-model="item.score"
+                  :min="0"
+                  :max="item.maxScore"
+                  :precision="1"
+                  :step="0.5"
+                  size="small"
+                  style="width: 100%; margin-top: 8px;"
+                />
+              </div>
+
+              <!-- 璇勮 -->
+              <div class="comment-section">
+                <h5>璇勮</h5>
+                <el-input
+                  v-model="ratingComment"
+                  type="textarea"
+                  :rows="4"
+                  placeholder="璇疯緭鍏ヨ瘎璇紙鍙�夛級"
+                  maxlength="500"
+                  show-word-limit
+                />
+              </div>
+
+              <!-- 鎻愪氦鎸夐挳 -->
+              <div class="submit-section">
+                <el-button type="primary" @click="handleSubmitRating" :loading="submitting" style="width: 100%;">
+                  鎻愪氦璇勫垎
+                </el-button>
+              </div>
+            </div>
+            <div v-else class="no-template">
+              <el-empty description="鏆傛棤璇勫垎妯℃澘" />
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+
+    <!-- 鏂囦欢棰勮瀵硅瘽妗� -->
+    <el-dialog v-model="previewVisible" title="鏂囦欢棰勮" width="80%" center>
+      <div class="preview-content">
+        <iframe 
+          v-if="previewUrl" 
+          :src="previewUrl" 
+          style="width: 100%; height: 500px; border: none;"
+        ></iframe>
+        <div v-else class="preview-error">
+          <el-empty description="鏃犳硶棰勮姝ゆ枃浠剁被鍨�" />
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, computed } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Document, UserFilled } from '@element-plus/icons-vue'
+import { getProjectDetail, getRatingStats, submitRating } from '@/api/projectReview'
+
+const route = useRoute()
+const router = useRouter()
+
+// 鍝嶅簲寮忔暟鎹�
+const loading = ref(false)
+const submitting = ref(false)
+const projectDetail = ref(null)
+const ratingStats = ref({ ratingCount: 0, averageScore: 0 })
+const ratingItems = ref([])
+const ratingComment = ref('')
+const previewVisible = ref(false)
+const previewUrl = ref('')
+
+// 璁$畻灞炴��
+const projectId = computed(() => route.params.id)
+
+// 鍔犺浇椤圭洰璇︽儏
+const loadProjectDetail = async () => {
+  loading.value = true
+  try {
+    const data = await getProjectDetail(projectId.value)
+    projectDetail.value = data
+    
+    // 鍒濆鍖栬瘎鍒嗛」
+    if (data.ratingForm && data.ratingForm.items) {
+      ratingItems.value = data.ratingForm.items.map(item => ({
+        ...item,
+        score: 0
+      }))
+    }
+  } catch (error) {
+    ElMessage.error('鍔犺浇椤圭洰璇︽儏澶辫触')
+    console.error(error)
+  } finally {
+    loading.value = false
+  }
+}
+
+// 鍔犺浇璇勫垎缁熻
+const loadRatingStats = async () => {
+  try {
+    const stats = await getRatingStats(projectId.value)
+    ratingStats.value = stats
+  } catch (error) {
+    console.error('鍔犺浇璇勫垎缁熻澶辫触:', error)
+  }
+}
+
+// 鎻愪氦璇勫垎
+const handleSubmitRating = async () => {
+  // 楠岃瘉璇勫垎
+  const hasEmptyScore = ratingItems.value.some(item => item.score === 0 || item.score === null)
+  if (hasEmptyScore) {
+    ElMessage.warning('璇蜂负鎵�鏈夎瘎鍒嗛」鎵撳垎')
+    return
+  }
+
+  try {
+    await ElMessageBox.confirm('纭畾瑕佹彁浜よ瘎鍒嗗悧锛熸彁浜ゅ悗灏嗘棤娉曚慨鏀广��', '纭鎻愪氦', {
+      confirmButtonText: '纭畾',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    })
+
+    submitting.value = true
+    
+    const ratingData = {
+      activityPlayerId: projectId.value,
+      ratings: ratingItems.value.map(item => ({
+        itemId: item.id,
+        score: item.score
+      })),
+      comment: ratingComment.value
+    }
+
+    await submitRating(ratingData)
+    ElMessage.success('璇勫垎鎻愪氦鎴愬姛')
+    
+    // 閲嶆柊鍔犺浇璇勫垎缁熻
+    await loadRatingStats()
+    
+  } catch (error) {
+    if (error !== 'cancel') {
+      ElMessage.error('璇勫垎鎻愪氦澶辫触')
+      console.error(error)
+    }
+  } finally {
+    submitting.value = false
+  }
+}
+
+// 鏂囦欢棰勮
+const previewFile = (file) => {
+  // 鏍规嵁鏂囦欢绫诲瀷鍐冲畾棰勮鏂瑰紡
+  const fileExtension = file.name.split('.').pop().toLowerCase()
+  const previewableTypes = ['pdf', 'txt', 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'mp4', 'avi', 'mov', 'wmv', 'flv', 'webm']
+  
+  if (previewableTypes.includes(fileExtension)) {
+    // 鍦ㄦ柊绐楀彛涓墦寮�棰勮
+    window.open(file.url, '_blank')
+  } else {
+    ElMessage.warning('姝ゆ枃浠剁被鍨嬩笉鏀寔棰勮锛岃涓嬭浇鏌ョ湅')
+  }
+}
+
+// 鏂囦欢涓嬭浇
+const downloadFile = (file) => {
+  const link = document.createElement('a')
+  link.href = file.url
+  link.download = file.name
+  document.body.appendChild(link)
+  link.click()
+  document.body.removeChild(link)
+}
+
+// 鏍煎紡鍖栨枃浠跺ぇ灏�
+const formatFileSize = (bytes) => {
+  if (!bytes) return '0 B'
+  const k = 1024
+  const sizes = ['B', 'KB', 'MB', 'GB']
+  const i = Math.floor(Math.log(bytes) / Math.log(k))
+  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStateType = (state) => {
+  const stateMap = {
+    0: 'danger',  // 宸叉嫆缁�
+    1: 'warning', // 寰呭鏍�
+    2: 'success', // 宸查�氳繃
+    3: 'info'     // 宸茬粨鏉�
+  }
+  return stateMap[state] || 'info'
+}
+
+// 鑾峰彇鐘舵�佸悕绉�
+const getStateName = (state) => {
+  const stateMap = {
+    0: '宸叉嫆缁�',
+    1: '寰呰瘎瀹�',
+    2: '宸查�氳繃',
+    3: '宸茬粨鏉�'
+  }
+  return stateMap[state] || '鏈煡'
+}
+
+// 杩斿洖涓婁竴椤�
+const goBack = () => {
+  router.back()
+}
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(() => {
+  loadProjectDetail()
+  loadRatingStats()
+})
+</script>
+
+<style scoped>
+.detail-container {
+  padding: 20px;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.card-title {
+  margin: 0;
+  font-size: 18px;
+  font-weight: 500;
+}
+
+.project-section h4,
+.rating-section h4 {
+  margin: 0 0 16px 0;
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+  border-left: 4px solid #409eff;
+  padding-left: 12px;
+}
+
+.description-content {
+  line-height: 1.6;
+  color: #606266;
+}
+
+.attachments {
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  padding: 16px;
+}
+
+.attachment-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.attachment-item:last-child {
+  border-bottom: none;
+}
+
+.file-info {
+  display: flex;
+  align-items: center;
+  flex: 1;
+}
+
+.file-icon {
+  margin-right: 8px;
+  color: #409eff;
+}
+
+.file-name {
+  font-weight: 500;
+  margin-right: 12px;
+}
+
+.file-size {
+  color: #909399;
+  font-size: 12px;
+}
+
+.file-actions {
+  display: flex;
+  gap: 8px;
+}
+
+.no-attachments {
+  text-align: center;
+  padding: 40px 0;
+}
+
+.rating-summary {
+  margin-bottom: 20px;
+}
+
+.rating-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12px;
+}
+
+.rating-item:last-child {
+  margin-bottom: 0;
+}
+
+.rating-item .label {
+  color: #606266;
+}
+
+.rating-item .value {
+  font-weight: 600;
+}
+
+.rating-item .score {
+  color: #67c23a;
+  font-size: 18px;
+}
+
+.rating-template {
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  padding: 16px;
+}
+
+.template-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16px;
+  padding-bottom: 12px;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.template-name {
+  font-weight: 600;
+  color: #303133;
+}
+
+.template-total {
+  color: #409eff;
+  font-weight: 600;
+}
+
+.template-item {
+  margin-bottom: 16px;
+  padding: 12px;
+  background-color: #f8f9fa;
+  border-radius: 4px;
+}
+
+.template-item:last-child {
+  margin-bottom: 0;
+}
+
+.item-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 8px;
+}
+
+.item-name {
+  font-weight: 500;
+  color: #303133;
+}
+
+.item-score {
+  color: #909399;
+  font-size: 12px;
+}
+
+.comment-section {
+  margin-top: 20px;
+}
+
+.comment-section h5 {
+  margin: 0 0 12px 0;
+  font-size: 14px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.submit-section {
+  margin-top: 20px;
+}
+
+.no-template {
+  text-align: center;
+  padding: 40px 0;
+}
+
+.preview-content {
+  text-align: center;
+}
+
+.preview-error {
+  padding: 40px 0;
+}
+
+:deep(.el-descriptions__label) {
+  font-weight: 600;
+}
+
+:deep(.el-card__body) {
+  padding: 16px;
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/project-review/index.vue b/web/src/views/project-review/index.vue
new file mode 100644
index 0000000..4420604
--- /dev/null
+++ b/web/src/views/project-review/index.vue
@@ -0,0 +1,317 @@
+<template>
+  <div class="review-container">
+    <el-card>
+      <template #header>
+        <div class="card-header">
+          <h3 class="card-title">椤圭洰璇勫</h3>
+        </div>
+      </template>
+
+      <el-form :inline="true" class="search-form">
+        <el-form-item label="閫夋嫨姣旇禌">
+          <el-select
+            v-model="selectedActivity"
+            placeholder="璇烽�夋嫨姣旇禌"
+            @change="handleActivityChange"
+            style="width: 300px"
+            clearable
+            filterable
+          >
+            <el-option
+              v-for="activity in activities"
+              :key="activity.id"
+              :label="getActivityDisplayName(activity)"
+              :value="activity.id"
+            >
+              <span>{{ getActivityName(activity) }}</span>
+              <span v-if="activity.pid > 0" style="color: #409eff; margin-left: 8px;">
+                {{ activity.name }}
+              </span>
+            </el-option>
+          </el-select>
+        </el-form-item>
+        
+        <el-form-item label="椤圭洰鍚嶇О">
+          <el-input
+            v-model="searchName"
+            placeholder="璇疯緭鍏ラ」鐩悕绉�"
+            @keyup.enter="loadProjects"
+            style="width: 200px"
+          />
+        </el-form-item>
+        
+        <el-form-item>
+          <el-button type="primary" @click="loadProjects" :loading="projectsLoading">
+            鎼滅储
+          </el-button>
+          <el-button @click="resetSearch">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+
+      <el-table 
+        :data="projects" 
+        style="width: 100%" 
+        v-loading="projectsLoading"
+        empty-text="璇峰厛閫夋嫨姣旇禌"
+      >
+        <el-table-column prop="playerName" label="椤圭洰鍚嶇О" min-width="150">
+          <template #default="scope">
+            {{ scope.row.projectName || scope.row.playerName }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="playerName" label="鍙傝禌浜哄鍚�" min-width="120" />
+        <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" min-width="120" />
+        <el-table-column prop="ratingCount" label="璇勫娆℃暟" width="100" align="center">
+          <template #default="scope">
+            <el-tag :type="scope.row.ratingCount > 0 ? 'success' : 'info'">
+              {{ scope.row.ratingCount }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="averageScore" label="骞冲潎鍒�" width="100" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.averageScore > 0" class="score">
+              {{ scope.row.averageScore.toFixed(1) }}
+            </span>
+            <span v-else class="no-score">鏈瘎鍒�</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="applyTime" label="鎶ュ悕鏃堕棿" width="180">
+          <template #default="scope">
+            {{ formatDate(scope.row.applyTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="state" label="鐘舵��" width="100" align="center">
+          <template #default="scope">
+            <el-tag :type="getStateType(scope.row.state)">
+              {{ getStateName(scope.row.state) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="120" fixed="right">
+          <template #default="scope">
+            <el-button type="primary" link @click="viewDetails(scope.row.id)">
+              璇︽儏璇勫
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 鍒嗛〉 -->
+      <div class="pagination-container" v-if="total > 0">
+        <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { ElMessage } from 'element-plus'
+import { getActiveActivities, getCompetitionProjects } from '@/api/projectReview'
+
+const router = useRouter()
+
+// 鍝嶅簲寮忔暟鎹�
+const activities = ref([])
+const selectedActivity = ref(null)
+const projects = ref([])
+const searchName = ref('')
+const activitiesLoading = ref(false)
+const projectsLoading = ref(false)
+
+// 鍒嗛〉鏁版嵁
+const currentPage = ref(1)
+const pageSize = ref(10)
+const total = ref(0)
+
+// 鍔犺浇姣旇禌鍒楄〃
+const loadActivities = async () => {
+  activitiesLoading.value = true
+  try {
+    const data = await getActiveActivities()
+    activities.value = data
+  } catch (error) {
+    ElMessage.error(error.message)
+  } finally {
+    activitiesLoading.value = false
+  }
+}
+
+// 鍔犺浇椤圭洰鍒楄〃
+const loadProjects = async () => {
+  if (!selectedActivity.value) {
+    ElMessage.warning('璇峰厛閫夋嫨姣旇禌')
+    return
+  }
+  
+  projectsLoading.value = true
+  try {
+    const response = await getCompetitionProjects(
+      selectedActivity.value,
+      currentPage.value - 1, // 鍚庣浠�0寮�濮�
+      pageSize.value,
+      searchName.value
+    )
+    
+    projects.value = response.content || []
+    total.value = response.totalElements || 0
+  } catch (error) {
+    console.error('鍔犺浇椤圭洰鍒楄〃澶辫触:', error)
+    ElMessage.error('鍔犺浇椤圭洰鍒楄〃澶辫触')
+    // 濡傛灉API璋冪敤澶辫触锛屾樉绀虹┖鏁版嵁
+    projects.value = []
+    total.value = 0
+  } finally {
+    projectsLoading.value = false
+  }
+}
+
+// 澶勭悊姣旇禌閫夋嫨鍙樺寲
+const handleActivityChange = (activityId) => {
+  currentPage.value = 1
+  loadProjects()
+}
+
+// 閲嶇疆鎼滅储
+const resetSearch = () => {
+  searchName.value = ''
+  currentPage.value = 1
+  if (selectedActivity.value) {
+    loadProjects()
+  }
+}
+
+// 鍒嗛〉澶勭悊
+const handleSizeChange = (size) => {
+  pageSize.value = size
+  currentPage.value = 1
+  loadProjects()
+}
+
+const handleCurrentChange = (page) => {
+  currentPage.value = page
+  loadProjects()
+}
+
+// 鏌ョ湅璇︽儏
+const viewDetails = (projectId) => {
+  router.push(`/project-review/${projectId}/detail`)
+}
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (dateString) => {
+  if (!dateString) return '-'
+  return new Date(dateString).toLocaleString('zh-CN')
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStateType = (state) => {
+  const stateMap = {
+    0: 'danger',  // 宸叉嫆缁�
+    1: 'warning', // 寰呭鏍�
+    2: 'success', // 宸查�氳繃
+    3: 'info'     // 宸茬粨鏉�
+  }
+  return stateMap[state] || 'info'
+}
+
+// 鑾峰彇鐘舵�佸悕绉�
+const getStateName = (state) => {
+  const stateMap = {
+    0: '宸叉嫆缁�',
+    1: '寰呰瘎瀹�',
+    2: '宸查�氳繃',
+    3: '宸茬粨鏉�'
+  }
+  return stateMap[state] || '鏈煡'
+}
+
+// 鑾峰彇姣旇禌鍚嶇О锛堝鏋滄槸闃舵锛岃繑鍥炵埗姣旇禌鍚嶇О锛涘鏋滄槸姣旇禌锛岃繑鍥炶嚜宸辩殑鍚嶇О锛�
+const getActivityName = (activity) => {
+  if (activity.pid > 0 && activity.parent) {
+    return activity.parent.name
+  }
+  return activity.name
+}
+
+// 鑾峰彇娲诲姩鏄剧ず鍚嶇О锛堢敤浜庢悳绱㈠拰閫変腑鏃舵樉绀猴級
+const getActivityDisplayName = (activity) => {
+  if (activity.pid > 0 && activity.parent) {
+    return `${activity.parent.name} - ${activity.name}`
+  }
+  return activity.name
+}
+
+// 鑾峰彇闃舵鍚嶇О锛堝鏋滄槸闃舵锛岃繑鍥為樁娈靛悕绉帮紱濡傛灉鏄瘮璧涳紝杩斿洖姣旇禌鍚嶇О锛�
+const getStageName = (activity) => {
+  return activity.name
+}
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(() => {
+  loadActivities()
+})
+</script>
+
+<style scoped>
+.review-container {
+  padding: 20px;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.card-title {
+  margin: 0;
+  font-size: 18px;
+  font-weight: 500;
+}
+
+.search-form {
+  margin-bottom: 20px;
+  padding: 20px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+}
+
+.score {
+  color: #67c23a;
+  font-weight: 600;
+}
+
+.no-score {
+  color: #909399;
+  font-style: italic;
+}
+
+.pagination-container {
+  margin-top: 20px;
+  display: flex;
+  justify-content: center;
+}
+
+:deep(.el-table) {
+  border-radius: 4px;
+}
+
+:deep(.el-table th) {
+  background-color: #fafafa;
+}
+
+:deep(.el-tag) {
+  border-radius: 12px;
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/region/index.vue b/web/src/views/region/index.vue
index f53f242..a47da85 100644
--- a/web/src/views/region/index.vue
+++ b/web/src/views/region/index.vue
@@ -34,7 +34,6 @@
 import { RegionApi } from '@/api/region'
 
 
-
 // 鏍戦厤缃紙鎳掑姞杞戒笉闇�瑕侀缃� children锛�
 const treeProps = {
   label: 'name',
diff --git a/web/src/views/review/detail.vue b/web/src/views/review/detail.vue
new file mode 100644
index 0000000..da57466
--- /dev/null
+++ b/web/src/views/review/detail.vue
@@ -0,0 +1,566 @@
+<template>
+  <div class="detail-container">
+    <el-card v-loading="loading">
+      <template #header>
+        <div class="card-header">
+          <h3 class="card-title">椤圭洰璇勫璇︽儏</h3>
+          <el-button @click="goBack">杩斿洖</el-button>
+        </div>
+      </template>
+
+      <el-row :gutter="20" v-if="projectDetail">
+        <!-- 宸︿晶锛氶」鐩俊鎭� -->
+        <el-col :span="16">
+          <div class="project-section">
+            <!-- 椤圭洰鍩烘湰淇℃伅 -->
+            <h4>椤圭洰淇℃伅</h4>
+            <el-descriptions :column="2" border>
+              <el-descriptions-item label="椤圭洰鍚嶇О">
+                {{ projectDetail.projectName || '鏈~鍐�' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="姣旇禌鍚嶇О">
+                {{ projectDetail.activityName }}
+              </el-descriptions-item>
+              <el-descriptions-item label="椤圭洰鎻忚堪" :span="2">
+                <div class="description-content">
+                  {{ projectDetail.description || '鏆傛棤鎻忚堪' }}
+                </div>
+              </el-descriptions-item>
+            </el-descriptions>
+
+            <!-- 椤圭洰闄勪欢 -->
+            <h4 style="margin-top: 20px;">椤圭洰闄勪欢</h4>
+            <div class="attachments" v-if="projectDetail.submissionFiles && projectDetail.submissionFiles.length > 0">
+              <div v-for="file in projectDetail.submissionFiles" :key="file.id" class="attachment-item">
+                <div class="file-info">
+                  <el-icon class="file-icon">
+                    <Document v-if="isDocument(file.mediaType)" />
+                    <Picture v-else-if="isImage(file.mediaType)" />
+                    <VideoPlay v-else-if="isVideo(file.mediaType)" />
+                    <Document v-else />
+                  </el-icon>
+                  <div class="file-details">
+                    <div class="file-name">{{ file.name }}</div>
+                    <div class="file-size">{{ formatFileSize(file.fileSize) }}</div>
+                  </div>
+                </div>
+                <div class="file-actions">
+                  <el-button 
+                    type="primary" 
+                    link 
+                    @click="previewFile(file)"
+                    v-if="canPreview(file.mediaType)"
+                  >
+                    棰勮
+                  </el-button>
+                  <el-button type="primary" link @click="downloadFile(file)">
+                    涓嬭浇
+                  </el-button>
+                </div>
+              </div>
+            </div>
+            <div v-else class="no-attachments">
+              <el-empty description="鏆傛棤闄勪欢" :image-size="80" />
+            </div>
+
+            <!-- 鍙傝禌浜轰俊鎭� -->
+            <h4 style="margin-top: 20px;">鍙傝禌浜轰俊鎭�</h4>
+            <el-descriptions :column="2" border v-if="projectDetail.playerInfo">
+              <el-descriptions-item label="澶村儚">
+                <el-avatar 
+                  :src="projectDetail.playerInfo.avatarUrl" 
+                  :size="60"
+                  :icon="UserFilled"
+                />
+              </el-descriptions-item>
+              <el-descriptions-item label="濮撳悕">
+                {{ projectDetail.playerInfo.name }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鎬у埆">
+                {{ projectDetail.playerInfo.gender || '鏈~鍐�' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鍑虹敓鏃ユ湡">
+                {{ formatDate(projectDetail.playerInfo.birthday) }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鐢佃瘽">
+                {{ projectDetail.playerInfo.phone }}
+              </el-descriptions-item>
+              <el-descriptions-item label="瀛﹀巻">
+                {{ projectDetail.playerInfo.education || '鏈~鍐�' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="鍖哄煙">
+                {{ projectDetail.regionInfo?.name || '鏈~鍐�' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="涓汉绠�浠�" :span="2">
+                <div class="bio-content">
+                  {{ projectDetail.playerInfo.introduction || '鏆傛棤绠�浠�' }}
+                </div>
+              </el-descriptions-item>
+            </el-descriptions>
+          </div>
+        </el-col>
+
+        <!-- 鍙充晶锛氳瘎鍒嗕俊鎭� -->
+        <el-col :span="8">
+          <div class="rating-section">
+            <!-- 璇勫缁熻 -->
+            <h4>璇勫淇℃伅</h4>
+            <el-card class="rating-summary">
+              <div class="rating-item">
+                <span class="label">宸茶瘎瀹℃鏁帮細</span>
+                <span class="value">{{ ratingStats.ratingCount }}</span>
+              </div>
+              <div class="rating-item">
+                <span class="label">褰撳墠骞冲潎鍒嗭細</span>
+                <span class="value score">
+                  {{ ratingStats.averageScore > 0 ? ratingStats.averageScore.toFixed(1) : '鏈瘎鍒�' }}
+                </span>
+              </div>
+            </el-card>
+
+            <!-- 璇勫垎妯℃澘 -->
+            <h4 style="margin-top: 20px;">璇勫垎妯℃澘</h4>
+            <div class="rating-template" v-if="projectDetail.ratingForm">
+              <div class="template-header">
+                <span class="template-name">{{ projectDetail.ratingForm.schemeName }}</span>
+                <span class="template-total">鎬诲垎锛歿{ projectDetail.ratingForm.totalMaxScore }}鍒�</span>
+              </div>
+              
+              <div v-for="item in ratingItems" :key="item.id" class="template-item">
+                <div class="item-header">
+                  <span class="item-name">{{ item.name }}</span>
+                  <span class="item-score">{{ item.maxScore }}鍒�</span>
+                </div>
+                <el-input-number
+                  v-model="item.score"
+                  :min="0"
+                  :max="item.maxScore"
+                  :precision="1"
+                  :step="0.5"
+                  style="width: 100%"
+                  @change="calculateTotalScore"
+                />
+              </div>
+              
+              <div class="total-score">
+                <span class="label">鎬诲垎锛�</span>
+                <span class="value">{{ totalScore }} / {{ projectDetail.ratingForm.totalMaxScore }}</span>
+              </div>
+            </div>
+
+            <!-- 璇勮 -->
+            <h4 style="margin-top: 20px;">璇勮</h4>
+            <el-input
+              v-model="comments"
+              type="textarea"
+              :rows="4"
+              placeholder="璇疯緭鍏ヨ瘎璇紙鍙�夛級"
+              maxlength="500"
+              show-word-limit
+            />
+
+            <!-- 鎻愪氦鎸夐挳 -->
+            <div class="submit-section">
+              <el-button 
+                type="primary" 
+                @click="submitRating" 
+                :loading="submitting"
+                :disabled="!canSubmit"
+                size="large"
+                style="width: 100%"
+              >
+                鎻愪氦璇勫垎
+              </el-button>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+
+    <!-- 鏂囦欢棰勮瀵硅瘽妗� -->
+    <el-dialog v-model="previewVisible" title="鏂囦欢棰勮" width="80%" center>
+      <div class="preview-content">
+        <img 
+          v-if="previewFile && isImage(previewFile.mediaType)" 
+          :src="previewFile.fullUrl" 
+          style="max-width: 100%; max-height: 500px;"
+        />
+        <video 
+          v-else-if="previewFile && isVideo(previewFile.mediaType)" 
+          :src="previewFile.fullUrl" 
+          controls 
+          style="max-width: 100%; max-height: 500px;"
+        />
+        <div v-else class="unsupported-preview">
+          <el-icon size="48"><Document /></el-icon>
+          <p>璇ユ枃浠剁被鍨嬩笉鏀寔棰勮锛岃涓嬭浇鍚庢煡鐪�</p>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Document, Picture, VideoPlay, UserFilled } from '@element-plus/icons-vue'
+import { getProjectDetail, getRatingStats, getCurrentJudgeRating, submitRating } from '@/api/projectReview'
+
+const router = useRouter()
+const route = useRoute()
+
+// 鍝嶅簲寮忔暟鎹�
+const loading = ref(true)
+const projectDetail = ref(null)
+const ratingStats = ref({ ratingCount: 0, averageScore: 0 })
+const ratingItems = ref([])
+const comments = ref('')
+const submitting = ref(false)
+const previewVisible = ref(false)
+const previewFile = ref(null)
+
+// 璁$畻灞炴��
+const totalScore = computed(() => {
+  return ratingItems.value.reduce((sum, item) => sum + (item.score || 0), 0)
+})
+
+const canSubmit = computed(() => {
+  return ratingItems.value.length > 0 && ratingItems.value.every(item => item.score >= 0)
+})
+
+// 鍔犺浇椤圭洰璇︽儏
+const loadProjectDetail = async () => {
+  try {
+    const projectId = route.params.id
+    const [detail, stats] = await Promise.all([
+      getProjectDetail(projectId),
+      getRatingStats(projectId)
+    ])
+    
+    projectDetail.value = detail
+    ratingStats.value = stats
+    
+    // 鍒濆鍖栬瘎鍒嗛」
+    if (detail.ratingForm && detail.ratingForm.items) {
+      ratingItems.value = detail.ratingForm.items.map(item => ({
+        ...item,
+        score: 0
+      }))
+    }
+    
+    // 鍔犺浇褰撳墠璇勫鐨勮瘎鍒嗭紙濡傛灉宸茶瘎鍒嗭級
+    try {
+      const currentRating = await getCurrentJudgeRating(projectId)
+      if (currentRating) {
+        comments.value = currentRating.comments || ''
+        // 濉厖宸叉湁璇勫垎
+        if (currentRating.ratingItems) {
+          currentRating.ratingItems.forEach(ratingItem => {
+            const item = ratingItems.value.find(i => i.id === ratingItem.itemId)
+            if (item) {
+              item.score = ratingItem.score
+            }
+          })
+        }
+      }
+    } catch (error) {
+      // 濡傛灉娌℃湁璇勫垎璁板綍锛屽拷鐣ラ敊璇�
+      console.log('鏈壘鍒板綋鍓嶈瘎濮旂殑璇勫垎璁板綍')
+    }
+    
+  } catch (error) {
+    ElMessage.error(error.message)
+  } finally {
+    loading.value = false
+  }
+}
+
+// 璁$畻鎬诲垎
+const calculateTotalScore = () => {
+  // 鎬诲垎璁$畻鍦╟omputed涓嚜鍔ㄥ畬鎴�
+}
+
+// 鎻愪氦璇勫垎
+const submitRating = async () => {
+  try {
+    await ElMessageBox.confirm('纭鎻愪氦璇勫垎鍚楋紵鎻愪氦鍚庝笉鍙慨鏀广��', '纭鎻愪氦', {
+      confirmButtonText: '纭',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    })
+    
+    submitting.value = true
+    
+    const ratingInput = {
+      activityPlayerId: route.params.id,
+      totalScore: totalScore.value,
+      comments: comments.value,
+      ratingItems: ratingItems.value.map(item => ({
+        itemId: item.id,
+        score: item.score
+      }))
+    }
+    
+    await submitRating(ratingInput)
+    
+    ElMessage.success('璇勫垎鎻愪氦鎴愬姛锛�')
+    
+    // 閲嶆柊鍔犺浇鏁版嵁
+    await loadProjectDetail()
+    
+  } catch (error) {
+    if (error !== 'cancel') {
+      ElMessage.error(error.message || '鎻愪氦璇勫垎澶辫触')
+    }
+  } finally {
+    submitting.value = false
+  }
+}
+
+// 鏂囦欢鐩稿叧鏂规硶
+const isImage = (mediaType) => {
+  return mediaType && mediaType.startsWith('image/')
+}
+
+const isVideo = (mediaType) => {
+  return mediaType && mediaType.startsWith('video/')
+}
+
+const isDocument = (mediaType) => {
+  return mediaType && (
+    mediaType.includes('pdf') || 
+    mediaType.includes('doc') || 
+    mediaType.includes('text')
+  )
+}
+
+const canPreview = (mediaType) => {
+  return isImage(mediaType) || isVideo(mediaType)
+}
+
+const formatFileSize = (size) => {
+  if (!size) return '鏈煡澶у皬'
+  const units = ['B', 'KB', 'MB', 'GB']
+  let index = 0
+  while (size >= 1024 && index < units.length - 1) {
+    size /= 1024
+    index++
+  }
+  return `${size.toFixed(1)} ${units[index]}`
+}
+
+const previewFile = (file) => {
+  previewFile.value = file
+  previewVisible.value = true
+}
+
+const downloadFile = (file) => {
+  window.open(file.fullUrl, '_blank')
+}
+
+const formatDate = (dateString) => {
+  if (!dateString) return '鏈~鍐�'
+  return new Date(dateString).toLocaleDateString('zh-CN')
+}
+
+const goBack = () => {
+  router.back()
+}
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(() => {
+  loadProjectDetail()
+})
+</script>
+
+<style scoped>
+.detail-container {
+  padding: 20px;
+}
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.card-title {
+  margin: 0;
+  font-size: 18px;
+  font-weight: 500;
+}
+
+.project-section {
+  padding-right: 20px;
+}
+
+.rating-section {
+  padding-left: 20px;
+}
+
+.description-content,
+.bio-content {
+  line-height: 1.6;
+  color: #606266;
+}
+
+.attachments {
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  padding: 16px;
+}
+
+.attachment-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.attachment-item:last-child {
+  border-bottom: none;
+}
+
+.file-info {
+  display: flex;
+  align-items: center;
+  flex: 1;
+}
+
+.file-icon {
+  margin-right: 12px;
+  font-size: 24px;
+  color: #409eff;
+}
+
+.file-details {
+  flex: 1;
+}
+
+.file-name {
+  font-weight: 500;
+  color: #303133;
+  margin-bottom: 4px;
+}
+
+.file-size {
+  font-size: 12px;
+  color: #909399;
+}
+
+.file-actions {
+  display: flex;
+  gap: 8px;
+}
+
+.no-attachments {
+  text-align: center;
+  padding: 40px 0;
+}
+
+.rating-summary {
+  margin-bottom: 20px;
+}
+
+.rating-item {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 12px;
+}
+
+.rating-item:last-child {
+  margin-bottom: 0;
+}
+
+.rating-item .label {
+  color: #606266;
+}
+
+.rating-item .value {
+  font-weight: 500;
+}
+
+.rating-item .score {
+  color: #409eff;
+  font-size: 18px;
+}
+
+.rating-template {
+  border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  padding: 16px;
+}
+
+.template-header {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 16px;
+  padding-bottom: 12px;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.template-name {
+  font-weight: 500;
+  color: #303133;
+}
+
+.template-total {
+  color: #409eff;
+  font-weight: 500;
+}
+
+.template-item {
+  margin-bottom: 16px;
+}
+
+.template-item:last-child {
+  margin-bottom: 0;
+}
+
+.item-header {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 8px;
+}
+
+.item-name {
+  font-weight: 500;
+  color: #303133;
+}
+
+.item-score {
+  color: #909399;
+  font-size: 14px;
+}
+
+.total-score {
+  display: flex;
+  justify-content: space-between;
+  margin-top: 16px;
+  padding-top: 12px;
+  border-top: 1px solid #f0f0f0;
+  font-weight: 500;
+  font-size: 16px;
+}
+
+.total-score .value {
+  color: #409eff;
+}
+
+.submit-section {
+  margin-top: 20px;
+}
+
+.preview-content {
+  text-align: center;
+}
+
+.unsupported-preview {
+  padding: 40px;
+  color: #909399;
+}
+
+.unsupported-preview p {
+  margin-top: 16px;
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/review/index.vue b/web/src/views/review/index.vue
new file mode 100644
index 0000000..2967d8c
--- /dev/null
+++ b/web/src/views/review/index.vue
@@ -0,0 +1,290 @@
+<template>
+  <div class="review-container">
+    <el-card>
+      <template #header>
+        <div class="card-header">
+          <h3 class="card-title">椤圭洰璇勫</h3>
+        </div>
+      </template>
+
+      <el-form :inline="true" class="search-form">
+        <el-form-item label="閫夋嫨姣旇禌">
+          <el-select 
+            v-model="selectedActivity" 
+            placeholder="璇烽�夋嫨姣旇禌" 
+            @change="handleActivityChange"
+            :loading="activitiesLoading"
+            style="width: 300px"
+          >
+            <el-option
+              v-for="item in activities"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        
+        <el-form-item label="椤圭洰鍚嶇О">
+          <el-input
+            v-model="searchName"
+            placeholder="璇疯緭鍏ラ」鐩悕绉�"
+            @keyup.enter="loadProjects"
+            style="width: 200px"
+          />
+        </el-form-item>
+        
+        <el-form-item>
+          <el-button type="primary" @click="loadProjects" :loading="projectsLoading">
+            鎼滅储
+          </el-button>
+          <el-button @click="resetSearch">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+
+      <el-table 
+        :data="projects" 
+        style="width: 100%" 
+        v-loading="projectsLoading"
+        empty-text="璇峰厛閫夋嫨姣旇禌"
+      >
+        <el-table-column prop="playerName" label="椤圭洰鍚嶇О" min-width="150">
+          <template #default="scope">
+            {{ scope.row.projectName || scope.row.playerName }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="playerName" label="鍙傝禌浜哄鍚�" min-width="120" />
+        <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" min-width="120" />
+        <el-table-column prop="ratingCount" label="璇勫娆℃暟" width="100" align="center">
+          <template #default="scope">
+            <el-tag :type="scope.row.ratingCount > 0 ? 'success' : 'info'">
+              {{ scope.row.ratingCount }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="averageScore" label="骞冲潎鍒�" width="100" align="center">
+          <template #default="scope">
+            <span v-if="scope.row.averageScore > 0" class="score">
+              {{ scope.row.averageScore.toFixed(1) }}
+            </span>
+            <span v-else class="no-score">鏈瘎鍒�</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="applyTime" label="鎶ュ悕鏃堕棿" width="180">
+          <template #default="scope">
+            {{ formatDate(scope.row.applyTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="state" label="鐘舵��" width="100" align="center">
+          <template #default="scope">
+            <el-tag :type="getStateType(scope.row.state)">
+              {{ getStateName(scope.row.state) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="120" fixed="right">
+          <template #default="scope">
+            <el-button type="primary" link @click="viewDetails(scope.row.id)">
+              璇︽儏璇勫
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 鍒嗛〉 -->
+      <div class="pagination-container" v-if="total > 0">
+        <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          :total="total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { ElMessage } from 'element-plus'
+import { getActiveActivities, getCompetitionProjects } from '@/api/projectReview'
+
+const router = useRouter()
+
+// 鍝嶅簲寮忔暟鎹�
+const activities = ref([])
+const selectedActivity = ref(null)
+const projects = ref([])
+const searchName = ref('')
+const activitiesLoading = ref(false)
+const projectsLoading = ref(false)
+
+// 鍒嗛〉鏁版嵁
+const currentPage = ref(1)
+const pageSize = ref(10)
+const total = ref(0)
+
+// 鍔犺浇姣旇禌鍒楄〃
+const loadActivities = async () => {
+  activitiesLoading.value = true
+  try {
+    const data = await getActiveActivities()
+    activities.value = data
+  } catch (error) {
+    ElMessage.error(error.message)
+  } finally {
+    activitiesLoading.value = false
+  }
+}
+
+// 鍔犺浇椤圭洰鍒楄〃
+const loadProjects = async () => {
+  if (!selectedActivity.value) {
+    ElMessage.warning('璇峰厛閫夋嫨姣旇禌')
+    return
+  }
+  
+  projectsLoading.value = true
+  try {
+    const response = await getCompetitionProjects(
+      selectedActivity.value,
+      currentPage.value - 1, // 鍚庣浠�0寮�濮�
+      pageSize.value,
+      searchName.value
+    )
+    
+    projects.value = response.content || []
+    total.value = response.totalElements || 0
+  } catch (error) {
+    console.error('鍔犺浇椤圭洰鍒楄〃澶辫触:', error)
+    ElMessage.error('鍔犺浇椤圭洰鍒楄〃澶辫触')
+    // 濡傛灉API璋冪敤澶辫触锛屾樉绀虹┖鏁版嵁
+    projects.value = []
+    total.value = 0
+  } finally {
+    projectsLoading.value = false
+  }
+}
+
+// 澶勭悊姣旇禌閫夋嫨鍙樺寲
+const handleActivityChange = (activityId) => {
+  currentPage.value = 1
+  loadProjects()
+}
+
+// 閲嶇疆鎼滅储
+const resetSearch = () => {
+  searchName.value = ''
+  currentPage.value = 1
+  if (selectedActivity.value) {
+    loadProjects()
+  }
+}
+
+// 鍒嗛〉澶勭悊
+const handleSizeChange = (size) => {
+  pageSize.value = size
+  currentPage.value = 1
+  loadProjects()
+}
+
+const handleCurrentChange = (page) => {
+  currentPage.value = page
+  loadProjects()
+}
+
+// 鏌ョ湅璇︽儏
+const viewDetails = (projectId) => {
+  router.push(`/review/${projectId}/detail`)
+}
+
+// 鏍煎紡鍖栨棩鏈�
+const formatDate = (dateString) => {
+  if (!dateString) return '-'
+  return new Date(dateString).toLocaleString('zh-CN')
+}
+
+// 鑾峰彇鐘舵�佺被鍨�
+const getStateType = (state) => {
+  const stateMap = {
+    0: 'danger',  // 宸叉嫆缁�
+    1: 'warning', // 寰呭鏍�
+    2: 'success', // 宸查�氳繃
+    3: 'info'     // 宸茬粨鏉�
+  }
+  return stateMap[state] || 'info'
+}
+
+// 鑾峰彇鐘舵�佸悕绉�
+const getStateName = (state) => {
+  const stateMap = {
+    0: '宸叉嫆缁�',
+    1: '寰呭鏍�',
+    2: '宸查�氳繃',
+    3: '宸茬粨鏉�'
+  }
+  return stateMap[state] || '鏈煡'
+}
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(() => {
+  loadActivities()
+})
+</script>
+
+<style scoped>
+.review-container {
+  padding: 20px;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.card-title {
+  margin: 0;
+  font-size: 18px;
+  font-weight: 500;
+}
+
+.search-form {
+  margin-bottom: 20px;
+  padding: 20px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+}
+
+.score {
+  color: #67c23a;
+  font-weight: 600;
+}
+
+.no-score {
+  color: #909399;
+  font-style: italic;
+}
+
+.pagination-container {
+  margin-top: 20px;
+  display: flex;
+  justify-content: center;
+}
+
+:deep(.el-table) {
+  border-radius: 4px;
+}
+
+:deep(.el-table th) {
+  background-color: #fafafa;
+}
+
+:deep(.el-tag) {
+  border-radius: 12px;
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/test/graphql-test.vue b/web/src/views/test/graphql-test.vue
new file mode 100644
index 0000000..47e64f6
--- /dev/null
+++ b/web/src/views/test/graphql-test.vue
@@ -0,0 +1,192 @@
+<template>
+  <div class="graphql-test">
+    <h2>GraphQL杩炴帴娴嬭瘯</h2>
+    
+    <div class="test-section">
+      <h3>娴嬭瘯1: 鍩烘湰杩炴帴娴嬭瘯</h3>
+      <button @click="testBasicConnection" :disabled="loading">
+        {{ loading ? '娴嬭瘯涓�...' : '娴嬭瘯鍩烘湰杩炴帴' }}
+      </button>
+      <div v-if="basicResult" class="result">
+        <h4>缁撴灉:</h4>
+        <pre>{{ JSON.stringify(basicResult, null, 2) }}</pre>
+      </div>
+      <div v-if="basicError" class="error">
+        <h4>閿欒:</h4>
+        <pre>{{ basicError }}</pre>
+      </div>
+    </div>
+
+    <div class="test-section">
+      <h3>娴嬭瘯2: 娲诲姩鍙傝禌鑰呰鎯呮煡璇�</h3>
+      <input v-model="testPlayerId" placeholder="杈撳叆鍙傝禌鑰匢D" />
+      <button @click="testPlayerDetail" :disabled="loading || !testPlayerId">
+        {{ loading ? '鏌ヨ涓�...' : '鏌ヨ鍙傝禌鑰呰鎯�' }}
+      </button>
+      <div v-if="playerResult" class="result">
+        <h4>缁撴灉:</h4>
+        <pre>{{ JSON.stringify(playerResult, null, 2) }}</pre>
+      </div>
+      <div v-if="playerError" class="error">
+        <h4>閿欒:</h4>
+        <pre>{{ playerError }}</pre>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import { graphqlRequest } from '@/config/api'
+
+const loading = ref(false)
+const basicResult = ref(null)
+const basicError = ref('')
+const playerResult = ref(null)
+const playerError = ref('')
+const testPlayerId = ref('1')
+
+// 浣跨敤缁熶竴鐨凣raphQL璇锋眰鍑芥暟
+
+// 娴嬭瘯鍩烘湰杩炴帴
+const testBasicConnection = async () => {
+  loading.value = true
+  basicResult.value = null
+  basicError.value = ''
+  
+  try {
+    const query = `
+      query {
+        __schema {
+          types {
+            name
+          }
+        }
+      }
+    `
+    
+    const result = await graphqlRequest(query)
+    basicResult.value = {
+      message: 'GraphQL杩炴帴鎴愬姛锛�',
+      typeCount: result.__schema.types.length,
+      sampleTypes: result.__schema.types.slice(0, 5).map((t: any) => t.name)
+    }
+  } catch (error) {
+    basicError.value = error instanceof Error ? error.message : '鏈煡閿欒'
+  } finally {
+    loading.value = false
+  }
+}
+
+// 娴嬭瘯鍙傝禌鑰呰鎯呮煡璇�
+const testPlayerDetail = async () => {
+  loading.value = true
+  playerResult.value = null
+  playerError.value = ''
+  
+  try {
+    const query = `
+      query ActivityPlayerDetail($id: ID!) {
+        activityPlayerDetail(id: $id) {
+          id
+          playerInfo {
+            id
+            name
+            phone
+            description
+            avatarUrl
+          }
+          regionInfo {
+            id
+            name
+            fullPath
+          }
+          activityName
+          description
+          submissionFiles {
+            id
+            name
+            url
+            fileExt
+            fileSize
+            mediaType
+          }
+        }
+      }
+    `
+    
+    const result = await graphqlRequest(query, { id: testPlayerId.value })
+    playerResult.value = result
+  } catch (error) {
+    playerError.value = error instanceof Error ? error.message : '鏈煡閿欒'
+  } finally {
+    loading.value = false
+  }
+}
+</script>
+
+<style scoped>
+.graphql-test {
+  padding: 20px;
+  max-width: 800px;
+  margin: 0 auto;
+}
+
+.test-section {
+  margin-bottom: 30px;
+  padding: 20px;
+  border: 1px solid #ddd;
+  border-radius: 8px;
+}
+
+.test-section h3 {
+  margin-top: 0;
+  color: #333;
+}
+
+button {
+  padding: 8px 16px;
+  margin: 10px 5px 10px 0;
+  background-color: #007bff;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+}
+
+button:disabled {
+  background-color: #ccc;
+  cursor: not-allowed;
+}
+
+input {
+  padding: 8px;
+  margin: 10px 5px 10px 0;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  width: 200px;
+}
+
+.result {
+  margin-top: 15px;
+  padding: 10px;
+  background-color: #d4edda;
+  border: 1px solid #c3e6cb;
+  border-radius: 4px;
+}
+
+.error {
+  margin-top: 15px;
+  padding: 10px;
+  background-color: #f8d7da;
+  border: 1px solid #f5c6cb;
+  border-radius: 4px;
+}
+
+pre {
+  white-space: pre-wrap;
+  word-wrap: break-word;
+  font-size: 12px;
+  margin: 5px 0 0 0;
+}
+</style>
\ No newline at end of file
diff --git a/wx/lib/cosUtil-example.md b/wx/lib/cosUtil-example.md
index 64508d6..d301b08 100644
--- a/wx/lib/cosUtil-example.md
+++ b/wx/lib/cosUtil-example.md
@@ -49,7 +49,7 @@
       storageType: 'COS',
       bucketName: 'ryc-1379367838',
       region: 'ap-chengdu',
-      targetType: 6, // STUDENT_AVATAR
+      targetType: 7, // USER_AVATAR
       targetId: registrationId
     })
     
diff --git a/wx/lib/cosUtil.js b/wx/lib/cosUtil.js
index 09220a5..1714af4 100644
--- a/wx/lib/cosUtil.js
+++ b/wx/lib/cosUtil.js
@@ -71,10 +71,16 @@
    * @returns {string} 鍞竴鏂囦欢鍚�
    */
   generateUniqueFileName(originalName) {
-    const timestamp = Date.now()
-    const random = Math.random().toString(36).substring(2, 8)
+    // 鐢熸垚绫讳技UUID鐨勫敮涓�鏍囪瘑绗�
+    const timestamp = Date.now().toString(36)
+    const random1 = Math.random().toString(36).substring(2, 10)
+    const random2 = Math.random().toString(36).substring(2, 10)
+    const random3 = Math.random().toString(36).substring(2, 6)
     const extension = originalName.substring(originalName.lastIndexOf('.'))
-    return `${timestamp}_${random}${extension}`
+    
+    // 鏍煎紡: timestamp-random1-random2-random3.ext
+    // 渚嬪: k8j2l3m4-a1b2c3d4-e5f6g7h8-i9j0.jpg
+    return `${timestamp}-${random1}-${random2}-${random3}${extension}`
   }
 
   /**
@@ -92,43 +98,55 @@
         return
       }
 
-      const cosConfig = app.globalData.cos
-      const uniqueFileName = this.generateUniqueFileName(originalName || 'file.jpg')
-      const key = this.generateFilePath(uniqueFileName, fileType)
+      // 棣栧厛鑾峰彇鏂囦欢淇℃伅锛堝寘鎷枃浠跺ぇ灏忥級
+      wx.getFileInfo({
+        filePath: filePath,
+        success: (fileInfo) => {
+          const cosConfig = app.globalData.cos
+          const uniqueFileName = this.generateUniqueFileName(originalName || 'file.jpg')
+          const key = this.generateFilePath(uniqueFileName, fileType)
 
-      console.log('寮�濮嬩笂浼犳枃浠跺埌COS:', {
-        filePath,
-        fileType,
-        key,
-        bucket: cosConfig.bucket
-      })
-
-      this.cos.uploadFile({
-        Bucket: cosConfig.bucket,
-        Region: cosConfig.region,
-        Key: key,
-        FilePath: filePath,
-        onProgress: (progressData) => {
-          const percent = Math.round(progressData.percent * 100)
-          console.log('涓婁紶杩涘害:', percent + '%')
-          if (onProgress && typeof onProgress === 'function') {
-            onProgress(percent)
-          }
-        }
-      }, (err, data) => {
-        if (err) {
-          console.error('COS涓婁紶澶辫触:', err)
-          reject(err)
-        } else {
-          console.log('COS涓婁紶鎴愬姛:', data)
-          resolve({
-            key: key,
-            url: `https://${data.Location}`,
-            etag: data.ETag,
-            fileName: uniqueFileName,
-            originalName: originalName,
-            fileType: fileType
+          console.log('寮�濮嬩笂浼犳枃浠跺埌COS:', {
+            filePath,
+            fileType,
+            key,
+            fileSize: fileInfo.size,
+            bucket: cosConfig.bucket
           })
+
+          this.cos.uploadFile({
+            Bucket: cosConfig.bucket,
+            Region: cosConfig.region,
+            Key: key,
+            FilePath: filePath,
+            onProgress: (progressData) => {
+              const percent = Math.round(progressData.percent * 100)
+              console.log('涓婁紶杩涘害:', percent + '%')
+              if (onProgress && typeof onProgress === 'function') {
+                onProgress(percent)
+              }
+            }
+          }, (err, data) => {
+            if (err) {
+              console.error('COS涓婁紶澶辫触:', err)
+              reject(err)
+            } else {
+              console.log('COS涓婁紶鎴愬姛:', data)
+              resolve({
+                key: key,
+                url: `https://${data.Location}`,
+                etag: data.ETag,
+                fileName: uniqueFileName,
+                originalName: originalName,
+                fileType: fileType,
+                fileSize: fileInfo.size
+              })
+            }
+          })
+        },
+        fail: (error) => {
+          console.error('鑾峰彇鏂囦欢淇℃伅澶辫触:', error)
+          reject(new Error('鑾峰彇鏂囦欢淇℃伅澶辫触: ' + error.errMsg))
         }
       })
     })
diff --git a/wx/pages/registration/registration.js b/wx/pages/registration/registration.js
index 6de900a..e797579 100644
--- a/wx/pages/registration/registration.js
+++ b/wx/pages/registration/registration.js
@@ -649,18 +649,21 @@
         this.setData({ attachments: updatedAttachments })
         
         try {
-          const uploadResult = await cosUtil.uploadFile(attachment.path, {
-            onProgress: (progress) => {
+          const uploadResult = await cosUtil.uploadFile(
+            attachment.path,
+            'attachment',
+            attachment.name || 'attachment',
+            (percent) => {
               // 鏇存柊涓婁紶杩涘害
               const progressAttachments = this.data.attachments.map(item => {
                 if (item.id === attachment.id) {
-                  return { ...item, progress: Math.round(progress.percent) }
+                  return { ...item, progress: Math.round(percent) }
                 }
                 return item
               })
               this.setData({ attachments: progressAttachments })
             }
-          })
+          )
           
           // 涓婁紶鎴愬姛
           const successAttachments = this.data.attachments.map(item => {
@@ -749,6 +752,141 @@
       wx.hideLoading()
       wx.showToast({
         title: '鍒犻櫎澶辫触',
+        icon: 'error'
+      })
+    }
+  },
+
+  // 棰勮闄勪欢
+  async onPreviewAttachment(e) {
+    try {
+      const index = e.currentTarget.dataset.index
+      const attachment = this.data.attachments[index]
+
+      if (!attachment.uploaded || !attachment.url) {
+        wx.showToast({
+          title: '鏂囦欢鏈笂浼犲畬鎴�',
+          icon: 'none'
+        })
+        return
+      }
+
+      const fileType = attachment.type
+
+      if (fileType === 'image') {
+        // 棰勮鍥剧墖 (media_type = 1)
+        wx.previewImage({
+          current: attachment.url,
+          urls: [attachment.url]
+        })
+      } else if (fileType === 'video') {
+        // 鎾斁瑙嗛 (media_type = 2) - 璺宠浆鍒拌棰戞挱鏀鹃〉闈�
+        wx.navigateTo({
+          url: `/pages/video/video?url=${encodeURIComponent(attachment.url)}&title=${encodeURIComponent(attachment.name)}`
+        })
+      } else if (fileType === 'pdf') {
+        // PDF鏂囦欢 (media_type = 4) - 浣跨敤灏忕▼搴忓唴缃殑鏂囨。棰勮
+        wx.showLoading({
+          title: '姝e湪鎵撳紑...',
+          mask: true
+        })
+        
+        wx.downloadFile({
+          url: attachment.url,
+          success: (res) => {
+            wx.hideLoading()
+            if (res.statusCode === 200) {
+              wx.openDocument({
+                filePath: res.tempFilePath,
+                fileType: 'pdf',
+                success: () => {
+                  console.log('PDF鎵撳紑鎴愬姛')
+                },
+                fail: (err) => {
+                  console.error('PDF鎵撳紑澶辫触:', err)
+                  wx.showToast({
+                    title: 'PDF鎵撳紑澶辫触',
+                    icon: 'none'
+                  })
+                }
+              })
+            } else {
+              wx.showToast({
+                title: '鏂囦欢涓嬭浇澶辫触',
+                icon: 'none'
+              })
+            }
+          },
+          fail: (err) => {
+            wx.hideLoading()
+            console.error('PDF涓嬭浇澶辫触:', err)
+            wx.showToast({
+              title: '鏂囦欢涓嬭浇澶辫触',
+              icon: 'none'
+            })
+          }
+        })
+      } else if (fileType === 'word' || fileType === 'excel' || fileType === 'ppt') {
+        // Office鏂囨。 (media_type = 4) - 浣跨敤灏忕▼搴忓唴缃殑鏂囨。棰勮
+        wx.showLoading({
+          title: '姝e湪鎵撳紑...',
+          mask: true
+        })
+        
+        wx.downloadFile({
+          url: attachment.url,
+          success: (res) => {
+            wx.hideLoading()
+            if (res.statusCode === 200) {
+              const fileTypeMap = {
+                'word': 'doc',
+                'excel': 'xls',
+                'ppt': 'ppt'
+              }
+              
+              wx.openDocument({
+                filePath: res.tempFilePath,
+                fileType: fileTypeMap[fileType] || 'doc',
+                success: () => {
+                  console.log('鏂囨。鎵撳紑鎴愬姛')
+                },
+                fail: (err) => {
+                  console.error('鏂囨。鎵撳紑澶辫触:', err)
+                  wx.showToast({
+                    title: '鏂囨。鎵撳紑澶辫触',
+                    icon: 'none'
+                  })
+                }
+              })
+            } else {
+              wx.showToast({
+                title: '鏂囦欢涓嬭浇澶辫触',
+                icon: 'none'
+              })
+            }
+          },
+          fail: (err) => {
+            wx.hideLoading()
+            console.error('鏂囨。涓嬭浇澶辫触:', err)
+            wx.showToast({
+              title: '鏂囦欢涓嬭浇澶辫触',
+              icon: 'none'
+            })
+          }
+        })
+      } else {
+        // 鍏朵粬鏂囦欢绫诲瀷 (media_type = 5) - 鎻愮ず鐢ㄦ埛
+        wx.showModal({
+          title: '鏂囦欢棰勮',
+          content: `鏆備笉鏀寔棰勮${fileType}绫诲瀷鐨勬枃浠讹紝璇蜂笅杞藉悗鏌ョ湅`,
+          showCancel: false,
+          confirmText: '鐭ラ亾浜�'
+        })
+      }
+    } catch (error) {
+      console.error('棰勮闄勪欢澶辫触:', error)
+      wx.showToast({
+        title: '棰勮澶辫触',
         icon: 'error'
       })
     }
@@ -904,13 +1042,15 @@
       })
 
       // 涓婁紶鍒癈OS
-      const uploadResult = await cosUtil.uploadAvatar(this.data.localAvatarPath, {
-        onProgress: (progress) => {
-          this.setData({
-            avatarUploadProgress: Math.round(progress.percent)
-          })
-        }
-      })
+      const uploadResult = await cosUtil.uploadAvatar(
+         this.data.localAvatarPath,
+         'avatar.jpg',
+         (percent) => {
+           this.setData({
+             avatarUploadProgress: Math.round(percent)
+           })
+         }
+       )
 
       // 涓婁紶鎴愬姛锛屾洿鏂拌〃鍗曟暟鎹�
       this.setData({
@@ -1092,18 +1232,23 @@
       })
 
       // 绗竴姝ワ細涓婁紶鍒癈OS
-      const uploadResult = await cosUtil.uploadAvatar(this.data.localAvatarPath, 'avatar.jpg', (progress) => {
-        this.setData({
-          avatarUploadProgress: Math.round(progress.percent)
-        })
-      })
+      const uploadResult = await cosUtil.uploadAvatar(
+         this.data.localAvatarPath,
+         'avatar.jpg',
+         (percent) => {
+           this.setData({
+             avatarUploadProgress: Math.round(percent)
+           })
+         }
+       )
 
       // 绗簩姝ワ細淇濆瓨濯掍綋璁板綍鍒版暟鎹簱
       await this.saveMediaRecord({
-        targetType: 7, // USER_AVATAR
+        targetType: 'player', // V2 浣跨敤瀛楃涓诧細player 琛ㄧず鐢ㄦ埛澶村儚锛屽搴� DB 鐨� 7 (USER_AVATAR)
         targetId: idInfo.userId,
-        url: uploadResult.url,
+        path: uploadResult.key,
         fileName: uploadResult.fileName || 'avatar.jpg',
+        fileExt: this.getFileExtension(uploadResult.fileName || 'avatar.jpg'),
         fileSize: uploadResult.fileSize,
         mediaType: 1 // 鍥剧墖
       })
@@ -1128,21 +1273,27 @@
   async uploadAttachmentsWithRegistrationId(idInfo) {
     for (let i = 0; i < this.data.attachments.length; i++) {
       const attachment = this.data.attachments[i]
-      if (!attachment.uploaded && attachment.localPath) {
+      if (!attachment.uploaded && attachment.path) {
         try {
           // 绗竴姝ワ細涓婁紶鍒癈OS
-          const uploadResult = await cosUtil.uploadFile(attachment.localPath, 'attachment', attachment.name || 'attachment', (progress) => {
-            this.setData({
-              [`attachments[${i}].uploadProgress`]: Math.round(progress.percent)
-            })
-          })
+          const uploadResult = await cosUtil.uploadFile(
+            attachment.path,
+            'attachment',
+            attachment.name || 'attachment',
+            (percent) => {
+              this.setData({
+                [`attachments[${i}].uploadProgress`]: Math.round(percent)
+              })
+            }
+          )
 
           // 绗簩姝ワ細淇濆瓨濯掍綋璁板綍鍒版暟鎹簱
           await this.saveMediaRecord({
-            targetType: 5, // ACTIVITY_PLAYER_SUBMISSION
+            targetType: 'activity_player', // V2 浣跨敤瀛楃涓诧細activity_player 琛ㄧず鎶ュ悕闄勪欢锛屽搴� DB 鐨� 5
             targetId: idInfo.activityPlayerId,
-            url: uploadResult.url,
+            path: uploadResult.key,
             fileName: uploadResult.fileName || attachment.name,
+            fileExt: this.getFileExtension(uploadResult.fileName || attachment.name),
             fileSize: uploadResult.fileSize,
             mediaType: this.getMediaType(attachment.name) // 鏍规嵁鏂囦欢鎵╁睍鍚嶅垽鏂被鍨�
           })
@@ -1167,11 +1318,11 @@
   // 淇濆瓨濯掍綋璁板綍鍒版暟鎹簱
   async saveMediaRecord(mediaData) {
     const mutation = `
-      mutation SaveMedia($input: MediaInput!) {
-        saveMedia(input: $input) {
-          id
-          url
-          fileName
+      mutation SaveMediaV2($input: MediaSaveInput!) {
+        saveMediaV2(input: $input) {
+          success
+          message
+          mediaId
         }
       }
     `
@@ -1180,8 +1331,9 @@
       input: {
         targetType: mediaData.targetType,
         targetId: mediaData.targetId,
-        url: mediaData.url,
-        fileName: mediaData.fileName,
+        path: mediaData.path,
+        fileName: mediaData.fileName || mediaData.name, // V2 瀛楁涓� fileName锛屽吋瀹规棫瀛楁 name
+        fileExt: mediaData.fileExt,
         fileSize: mediaData.fileSize,
         mediaType: mediaData.mediaType
       }
@@ -1189,14 +1341,20 @@
     
     try {
       const result = await app.graphqlRequest(mutation, variables)
-      console.log('濯掍綋璁板綍淇濆瓨鎴愬姛:', result.saveMedia)
-      return result.saveMedia
+      console.log('濯掍綋璁板綍淇濆瓨鎴愬姛(V2):', result.saveMediaV2)
+      return result.saveMediaV2
     } catch (error) {
-      console.error('濯掍綋璁板綍淇濆瓨澶辫触:', error)
+      console.error('濯掍綋璁板綍淇濆瓨澶辫触(V2):', error)
       throw error
     }
   },
 
+  // 鑾峰彇鏂囦欢鎵╁睍鍚�
+  getFileExtension(fileName) {
+    if (!fileName) return ''
+    return fileName.split('.').pop().toLowerCase()
+  },
+
   // 鏍规嵁鏂囦欢鍚嶈幏鍙栧獟浣撶被鍨�
   getMediaType(fileName) {
     if (!fileName) return 1 // 榛樿鍥剧墖
diff --git a/wx/pages/registration/registration.wxml b/wx/pages/registration/registration.wxml
index a0cdaba..5722221 100644
--- a/wx/pages/registration/registration.wxml
+++ b/wx/pages/registration/registration.wxml
@@ -280,6 +280,7 @@
               </view>
             </view>
             <view class="attachment-actions">
+              <text wx:if="{{!item.uploading && item.uploaded}}" class="action-btn preview" bindtap="onPreviewAttachment" data-index="{{index}}">棰勮</text>
               <text wx:if="{{!item.uploading}}" class="action-btn delete" bindtap="onDeleteAttachment" data-index="{{index}}">鍒犻櫎</text>
             </view>
           </view>
diff --git a/wx/pages/registration/registration.wxss b/wx/pages/registration/registration.wxss
index d9c4192..087ca4d 100644
--- a/wx/pages/registration/registration.wxss
+++ b/wx/pages/registration/registration.wxss
@@ -841,6 +841,12 @@
   transition: all 0.3s ease;
 }
 
+.attachment-actions .action-btn.preview {
+  color: #1976d2;
+  background: #f3f8ff;
+  margin-right: 8rpx;
+}
+
 .attachment-actions .action-btn.delete {
   color: #dc3545;
   background: #fff5f5;
diff --git "a/\350\205\276\350\256\257\344\272\221COS\346\226\207\346\241\243\351\242\204\350\247\210\346\226\271\346\241\210\350\260\203\347\240\224.md" "b/\350\205\276\350\256\257\344\272\221COS\346\226\207\346\241\243\351\242\204\350\247\210\346\226\271\346\241\210\350\260\203\347\240\224.md"
new file mode 100644
index 0000000..aab9857
--- /dev/null
+++ "b/\350\205\276\350\256\257\344\272\221COS\346\226\207\346\241\243\351\242\204\350\247\210\346\226\271\346\241\210\350\260\203\347\240\224.md"
@@ -0,0 +1,107 @@
+# 鑵捐浜慍OS鏂囨。棰勮鏂规璋冪爺
+
+## 璋冪爺鑳屾櫙
+涓轰簡瀹炵幇灏忕▼搴忎腑闄勪欢锛圥DF銆乄ord銆丒xcel銆丳PT绛夛級鐨勫湪绾块瑙堝姛鑳斤紝闇�瑕佽皟鐮旇吘璁簯COS鐩稿叧鐨勬枃妗i瑙堣В鍐虫柟妗堛��
+
+## 璋冪爺缁撴灉
+
+### 1. 鑵捐浜慍OS鍘熺敓鏀寔
+鑵捐浜慍OS鏈韩涓昏鎻愪緵瀵硅薄瀛樺偍鏈嶅姟锛屼笉鐩存帴鎻愪緵鏂囨。棰勮鍔熻兘銆備絾鍙互缁撳悎鍏朵粬鏈嶅姟瀹炵幇鏂囨。棰勮銆�
+
+### 2. 灏忕▼搴忓師鐢熸敮鎸�
+寰俊灏忕▼搴忔彁渚涗簡鍩虹鐨勬枃妗i瑙堣兘鍔涳細
+- **wx.openDocument**: 鏀寔鎵撳紑PDF銆乄ord銆丒xcel銆丳PT绛夋枃妗�
+- **wx.previewImage**: 鏀寔鍥剧墖棰勮
+- **video缁勪欢**: 鏀寔瑙嗛鎾斁
+
+### 3. 绗笁鏂规枃妗i瑙堟柟妗�
+
+#### 3.1 寰蒋Office Online棰勮鏈嶅姟
+- **鏈嶅姟鍦板潃**: `http://view.officeapps.live.com/op/view.aspx?src=鏂囨。URL`
+- **鏀寔鏍煎紡**: Word(docx, dotx)銆丒xcel(xlsx, xlsb, xls, xlsm)銆丳owerPoint(pptx, ppsx, ppt, pps, potx, ppsm)
+- **闄愬埗鏉′欢**:
+  - 鏂囨。蹇呴』澶栫綉鍙闂�
+  - Word鍜孭owerPoint鏂囨。灏忎簬10MB
+  - Excel鏂囨。灏忎簬5MB
+  - 鏂囨。URL蹇呴』鏄煙鍚嶄笖涓�80绔彛
+- **瀹夊叏椋庨櫓**: 鏂囨。鍙兘琚笂浼犲埌寰蒋鏈嶅姟鍣�
+
+#### 3.2 寮�婧愭枃妗i瑙堟湇鍔� (kkFileView)
+- **椤圭洰鍦板潃**: https://gitee.com/kekingcn/file-online-preview
+- **鏀寔鏍煎紡**: doc銆乨ocx銆乸pt銆乸ptx銆亁ls銆亁lsx銆乸df銆亃ip銆乺ar銆乵p4銆乵p3銆乼xt绛�
+- **閮ㄧ讲鏂瑰紡**: Spring Boot搴旂敤锛岄渶瑕佽嚜琛岄儴缃�
+- **渚濊禆鐜**: Redis(鍙��)銆丱penOffice鎴朙ibreOffice
+
+#### 3.3 Vue-Office缁勪欢搴�
+- **閫傜敤鍦烘櫙**: 鍓嶇鐩存帴棰勮
+- **鏀寔鏍煎紡**: docx銆亁lsx銆乸df銆乸ptx
+- **浣跨敤鏂瑰紡**: 
+  - 缃戠粶鍦板潃棰勮
+  - ArrayBuffer/Blob鏁版嵁棰勮
+  - 鏂囦欢涓婁紶棰勮
+
+### 4. 鎺ㄨ崘鏂规
+
+#### 鏂规涓�锛氬皬绋嬪簭鍘熺敓 + 寰蒋Office Online锛堟帹鑽愶級
+```javascript
+// 鍥剧墖棰勮
+wx.previewImage({
+  current: imageUrl,
+  urls: [imageUrl]
+})
+
+// 瑙嗛鎾斁
+wx.navigateTo({
+  url: `/pages/video/video?url=${encodeURIComponent(videoUrl)}`
+})
+
+// Office鏂囨。棰勮锛堜娇鐢ㄥ井杞湇鍔★級
+const officeUrl = `http://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(docUrl)}`
+wx.navigateTo({
+  url: `/pages/webview/webview?url=${encodeURIComponent(officeUrl)}`
+})
+
+// PDF鏂囨。棰勮
+wx.downloadFile({
+  url: pdfUrl,
+  success: (res) => {
+    wx.openDocument({
+      filePath: res.tempFilePath
+    })
+  }
+})
+```
+
+#### 鏂规浜岋細鑷缓鏂囨。棰勮鏈嶅姟
+1. 閮ㄧ讲kkFileView鏈嶅姟
+2. 灏咰OS鏂囨。URL浼犻�掔粰棰勮鏈嶅姟
+3. 鍦ㄥ皬绋嬪簭涓�氳繃webview鎵撳紑棰勮椤甸潰
+
+#### 鏂规涓夛細鍓嶇鐩存帴棰勮锛堥檺鍒惰緝澶氾級
+浣跨敤Vue-Office绛夊墠绔粍浠跺簱锛屼絾闇�瑕佽�冭檻灏忕▼搴忕幆澧冪殑鍏煎鎬с��
+
+## 瀹炴柦寤鸿
+
+### 鐭湡鏂规锛堢珛鍗冲彲鐢級
+1. **鍥剧墖**: 浣跨敤`wx.previewImage`
+2. **瑙嗛**: 浣跨敤鑷畾涔夎棰戞挱鏀鹃〉闈�
+3. **PDF**: 浣跨敤`wx.openDocument`涓嬭浇鍚庨瑙�
+4. **Office鏂囨。**: 浣跨敤寰蒋Office Online鏈嶅姟锛堥渶瑕佹枃妗e缃戝彲璁块棶锛�
+
+### 闀挎湡鏂规锛堟洿濂界殑鐢ㄦ埛浣撻獙锛�
+1. 閮ㄧ讲鑷缓鐨勬枃妗i瑙堟湇鍔★紙濡俴kFileView锛�
+2. 纭繚鏂囨。鐨勫缃戣闂兘鍔�
+3. 鍦ㄥ皬绋嬪簭涓泦鎴恮ebview缁勪欢鐢ㄤ簬鏂囨。棰勮
+
+## 瀹夊叏鑰冭檻
+1. 浣跨敤寰蒋Office Online鏈嶅姟鏃讹紝鏂囨。鍙兘琚笂浼犲埌寰蒋鏈嶅姟鍣�
+2. 鑷缓棰勮鏈嶅姟鍙互鏇村ソ鍦版帶鍒舵暟鎹畨鍏�
+3. 鏁忔劅鏂囨。寤鸿浣跨敤鏈湴涓嬭浇棰勮鏂瑰紡
+
+## 鎴愭湰鍒嗘瀽
+1. **寰蒋Office Online**: 鍏嶈垂锛屼絾鏈夊畨鍏ㄩ闄�
+2. **鑷缓鏈嶅姟**: 闇�瑕佹湇鍔″櫒璧勬簮鍜岀淮鎶ゆ垚鏈�
+3. **灏忕▼搴忓師鐢�**: 鍏嶈垂锛屼絾鍔熻兘鏈夐檺
+
+## 缁撹
+寤鸿閲囩敤**鏂规涓�**浣滀负鍒濇湡瀹炵幇锛屽悗缁牴鎹笟鍔¢渶姹傚拰瀹夊叏瑕佹眰鑰冭檻閮ㄧ讲鑷缓鏂囨。棰勮鏈嶅姟銆�
\ No newline at end of file
diff --git "a/\351\241\265\351\235\242bug.png" "b/\351\241\265\351\235\242bug.png"
new file mode 100644
index 0000000..5f76a66
--- /dev/null
+++ "b/\351\241\265\351\235\242bug.png"
Binary files differ

--
Gitblit v1.8.0