From 375c18a6d2713ff19b22093eec57315992d8333f Mon Sep 17 00:00:00 2001
From: Codex Assistant <codex@example.com>
Date: 星期四, 06 十一月 2025 13:33:52 +0800
Subject: [PATCH] 增加评审下载

---
 tmp/review-2/README.txt                                                               |    5 
 backend/src/main/java/com/rongyichuang/activity/entity/Activity.java                  |   14 
 tmp/review-export/players/海选/docx-xml/[Content_Types].xml                             |    1 
 backend/src/main/java/com/rongyichuang/review/dto/response/ReviewExportResponse.java  |   42 +
 tmp/review-2/players/海选/12-匿名用户.docx                                                  |    0 
 wx/pages/activity/detail.wxml                                                         |    6 
 web/src/views/activity-list.vue                                                       |   27 
 doc/12-匿名用户.docx                                                                      |    0 
 doc/~$选评分表)2025年成渝德眉资创业大赛海选.doc                                                       |    0 
 backend/src/main/java/com/rongyichuang/review/dto/response/ReviewExportJobStatus.java |   44 +
 tmp/docx-海选-12/word/settings.xml                                                      |    2 
 tmp/review-export/players/海选/12-匿名用户.docx                                             |    0 
 backend/src/main/java/com/rongyichuang/review/service/ReviewExportService.java        |  432 ++++++++++++++++
 doc/(海选评分表)2025年成渝德眉资创业大赛海选.doc                                                       |    0 
 wx/pages/registration/registration.js                                                 |    8 
 tmp/docx-海选-12-new/word/document.xml                                                  |    2 
 wx/pages/activity/detail.js                                                           |  113 ++++
 tmp/docx-海选-12-new/_rels/.rels                                                        |    1 
 tmp/docx-海选-12-new/docProps/app.xml                                                   |    2 
 tmp/review-export/players/海选/docx-xml/word/_rels/document.xml.rels                    |    1 
 tmp/docx-海选-12-new/docProps/core.xml                                                  |    1 
 tmp/docx-海选-12-new/word/_rels/document.xml.rels                                       |    1 
 tmp/docx-海选-12/[Content_Types].xml                                                    |    1 
 tmp/docx-海选-12/word/document.xml                                                      |    2 
 tmp/review-export-2/players/海选/12-匿名用户.docx                                           |    0 
 backend/src/main/resources/graphql/activity.graphqls                                  |    1 
 tmp/review-export/players/海选/docx-xml/_rels/.rels                                     |    1 
 tmp/review-export/players/海选/docx-xml/word/document.xml                               |    2 
 tmp/docx-海选-12/docProps/app.xml                                                       |    2 
 web/src/api/activity.js                                                               |    2 
 tmp/docx-layout-sample.docx                                                           |    0 
 tmp/docx-海选-12/word/_rels/document.xml.rels                                           |    1 
 backend/src/main/java/com/rongyichuang/config/SecurityConfig.java                     |    3 
 tmp/review-export/README.txt                                                          |    7 
 backend/src/main/java/com/rongyichuang/judge/service/CosService.java                  |   33 +
 web/vite.config.ts                                                                    |   15 
 tmp/review-export/players/海选/docx-xml/docProps/core.xml                               |    1 
 backend/src/main/java/com/rongyichuang/activity/dto/ActivityResponse.java             |   11 
 backend/src/main/java/com/rongyichuang/review/service/ReviewExportJobService.java     |   66 ++
 tmp/review-export-2/README.txt                                                        |    7 
 web/src/api/reviewExport.js                                                           |   70 ++
 tmp/docx-海选-12/docProps/core.xml                                                      |    1 
 tmp/review-0/README.txt                                                               |    4 
 web/src/views/ActivityDetail.vue                                                      |  180 ++++++
 tmp/docx-海选-12-new/[Content_Types].xml                                                |    1 
 tmp/docx-海选-12-new/word/settings.xml                                                  |    2 
 backend/src/main/java/com/rongyichuang/review/resolver/ReviewResolver.java            |   48 +
 wx/pages/registration/registration.wxml                                               |   23 
 tmp/review-0/players/海选/12-匿名用户.docx                                                  |    0 
 tmp/review-export/players/海选/docx-xml/docProps/app.xml                                |    2 
 /dev/null                                                                             |  156 ------
 backend/pom.xml                                                                       |   38 
 tmp/review-1/README.txt                                                               |    5 
 backend/src/test/java/com/rongyichuang/review/DocxLayoutTest.java                     |  114 ++++
 tmp/docx-海选-12/_rels/.rels                                                            |    1 
 backend/src/main/resources/graphql/review.graphqls                                    |   35 +
 tmp/review-export/players/海选/docx-xml/word/settings.xml                               |    2 
 57 files changed, 1,326 insertions(+), 213 deletions(-)

diff --git a/PasswordTest.java b/PasswordTest.java
deleted file mode 100644
index cccdc80..0000000
--- a/PasswordTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-
-public class PasswordTest {
-    public static void main(String[] args) {
-        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
-        String hashedPassword = "$2a$10$VBHNbQlhM1OnQ8QTLkEVSeXBfLAlD9AJqNjErsYC664SUzMZZxjp.";
-        String plainPassword = "123456";
-        
-        boolean matches = encoder.matches(plainPassword, hashedPassword);
-        System.out.println("Password matches: " + matches);
-        
-        // 涔熸祴璇曚竴涓嬫柊鐢熸垚鐨勫瘑鐮�
-        String newHash = encoder.encode(plainPassword);
-        System.out.println("New hash: " + newHash);
-        System.out.println("New hash matches: " + encoder.matches(plainPassword, newHash));
-    }
-}

diff --git a/backend/pom.xml b/backend/pom.xml
index 18701af..dfcd6f6 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -146,6 +146,18 @@
             <artifactId>spring-security-test</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <!-- Apache POI for Word/Excel document generation -->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>5.2.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>5.2.5</version>
+        </dependency>
     </dependencies>
 
     <build>
@@ -155,8 +167,16 @@
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
                 <configuration>
-                    <skip>true</skip>
+                    <!-- 鐢熸垚鍙墽琛岀殑鑳栧寘锛屼究浜庢湰鍦扮洿鎺ヨ繍琛� -->
+                    <skip>false</skip>
                 </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
             </plugin>
 
             <!-- 鍦ㄦ墦鍖呴樁娈靛鍒舵墍鏈変緷璧栧埌 target/lib -->
@@ -182,21 +202,7 @@
                 </executions>
             </plugin>
 
-            <!-- 鐢熸垚鍙墽琛岀槮 JAR锛氬啓鍏� Main-Class 涓� Class-Path 鎸囧悜 lib/ -->
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-jar-plugin</artifactId>
-                <version>3.4.2</version>
-                <configuration>
-                    <archive>
-                        <manifest>
-                            <addClasspath>true</addClasspath>
-                            <classpathPrefix>lib/</classpathPrefix>
-                            <mainClass>com.rongyichuang.RycBackendApplication</mainClass>
-                        </manifest>
-                    </archive>
-                </configuration>
-            </plugin>
+            <!-- 浣跨敤 Spring Boot repackage 鐢熸垚鍙墽琛岃儢鍖咃紝绉婚櫎鑷畾涔� Jar 娓呭崟浠ラ伩鍏嶅啿绐� -->
         </plugins>
     </build>
 </project>
\ No newline at end of file
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 b603d44..f80d70f 100644
--- a/backend/src/main/java/com/rongyichuang/activity/dto/ActivityResponse.java
+++ b/backend/src/main/java/com/rongyichuang/activity/dto/ActivityResponse.java
@@ -23,6 +23,8 @@
     private Integer state;
     private LocalDateTime createTime;
     private LocalDateTime updateTime;
+    // 鏈�杩戜竴娆¤瘎瀹″鍑篫IP涓嬭浇URL
+    private String reviewExportUrl;
     
     // 鍏宠仈鏁版嵁
     private RatingSchemeResponse ratingScheme;
@@ -57,6 +59,7 @@
         this.state = activity.getState();
         this.createTime = activity.getCreateTime();
         this.updateTime = activity.getUpdateTime();
+        this.reviewExportUrl = activity.getReviewExportUrl();
         
         // 璁剧疆鐘舵�佸悕绉�
         this.stateName = getStateNameByValue(activity.getState());
@@ -191,6 +194,14 @@
     public void setUpdateTime(LocalDateTime updateTime) {
         this.updateTime = updateTime;
     }
+
+    public String getReviewExportUrl() {
+        return reviewExportUrl;
+    }
+
+    public void setReviewExportUrl(String reviewExportUrl) {
+        this.reviewExportUrl = reviewExportUrl;
+    }
     
     public RatingSchemeResponse getRatingScheme() {
         return ratingScheme;
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 41ac1e7..5ceeec0 100644
--- a/backend/src/main/java/com/rongyichuang/activity/entity/Activity.java
+++ b/backend/src/main/java/com/rongyichuang/activity/entity/Activity.java
@@ -51,6 +51,12 @@
      */
     @Column(name = "state", nullable = false)
     private Integer state = 1;
+
+    /**
+     * 璇勫瀵煎嚭ZIP涓嬭浇URL锛堟渶杩戜竴娆″鍑猴級
+     */
+    @Column(name = "review_export_url", length = 512)
+    private String reviewExportUrl;
     
     // 鍏宠仈璇勫垎妯℃澘
     @ManyToOne(fetch = FetchType.LAZY)
@@ -195,6 +201,14 @@
     public void setState(Integer state) {
         this.state = state;
     }
+
+    public String getReviewExportUrl() {
+        return reviewExportUrl;
+    }
+
+    public void setReviewExportUrl(String reviewExportUrl) {
+        this.reviewExportUrl = reviewExportUrl;
+    }
     
     // 涓氬姟鏂规硶
     public boolean isMainActivity() {
diff --git a/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java b/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
index 0accc67..92af34a 100644
--- a/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
+++ b/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
@@ -48,7 +48,8 @@
             .csrf(csrf -> csrf.disable())
             .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
             .authorizeHttpRequests(auth -> auth
-                .requestMatchers("/auth/**", "/actuator/**", "/test/**", "/cleanup/**").permitAll()
+                // 娉ㄦ剰锛氬簲鐢ㄨ缃簡 context-path=/api锛屼负閬垮厤鍖归厤姝т箟锛岃繖閲屽悓鏃跺尮閰嶅幓闄ゅ拰鍖呭惈 context-path 鐨勮矾寰�
+                .requestMatchers("/auth/**", "/api/auth/**", "/actuator/**", "/test/**", "/cleanup/**").permitAll()
                 .requestMatchers("/api/health/**").permitAll() // 鍏佽鍋ュ悍妫�鏌ョ鐐硅闂�
                 .requestMatchers("/upload/**").permitAll()
                 .requestMatchers("/graphiql/**", "/graphql/**", "/api/graphql/**", "/api/graphiql/**").permitAll() // 鍏佽GraphQL鍜孏raphiQL璁块棶
diff --git a/backend/src/main/java/com/rongyichuang/judge/service/CosService.java b/backend/src/main/java/com/rongyichuang/judge/service/CosService.java
index 4e0273e..44bc63d 100644
--- a/backend/src/main/java/com/rongyichuang/judge/service/CosService.java
+++ b/backend/src/main/java/com/rongyichuang/judge/service/CosService.java
@@ -124,6 +124,39 @@
     }
 
     /**
+     * 鐩存帴涓婁紶鏈湴鏂囦欢鍒癈OS
+     */
+    public String uploadLocalFile(java.io.File file, String fileName) throws Exception {
+        // 鐢熸垚鏂囦欢璺緞锛氭寜鏃ユ湡鍒嗙洰褰�
+        String dateDir = new java.text.SimpleDateFormat("yyyyMMdd").format(new Date());
+        String key = dateDir + "/" + fileName;
+
+        System.out.println("=== COS鏈湴鏂囦欢涓婁紶璋冭瘯淇℃伅 ===");
+        System.out.println("鏂囦欢Key: " + key);
+        System.out.println("鏂囦欢澶у皬: " + file.length());
+        System.out.println("鏂囦欢璺緞: " + file.getAbsolutePath());
+
+        // 鍒涘缓COS瀹㈡埛绔�
+        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
+        ClientConfig clientConfig = new ClientConfig(new Region(region));
+        COSClient cosClient = new COSClient(cred, clientConfig);
+
+        try {
+            // 鍒涘缓涓婁紶璇锋眰
+            com.qcloud.cos.model.PutObjectRequest putObjectRequest =
+                new com.qcloud.cos.model.PutObjectRequest(bucket, key, file);
+
+            // 鎵ц涓婁紶
+            com.qcloud.cos.model.PutObjectResult result = cosClient.putObject(putObjectRequest);
+            System.out.println("涓婁紶鎴愬姛锛孍Tag: " + result.getETag());
+
+            return key; // 杩斿洖鐩稿璺緞
+        } finally {
+            cosClient.shutdown();
+        }
+    }
+
+    /**
      * 鑾峰彇鏂囦欢璁块棶 URL
      */
     public String getFileUrl(String key) {
diff --git a/backend/src/main/java/com/rongyichuang/review/dto/response/ReviewExportJobStatus.java b/backend/src/main/java/com/rongyichuang/review/dto/response/ReviewExportJobStatus.java
new file mode 100644
index 0000000..e96bbd3
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/review/dto/response/ReviewExportJobStatus.java
@@ -0,0 +1,44 @@
+package com.rongyichuang.review.dto.response;
+
+/**
+ * 璇勫瀵煎嚭浠诲姟鐘舵�佸搷搴�
+ */
+public class ReviewExportJobStatus {
+    public enum Status {
+        PENDING,
+        RUNNING,
+        SUCCEEDED,
+        FAILED
+    }
+
+    private String jobId;
+    private Status status;
+    private String url;
+    private String message;
+    private Integer progress; // 0-100锛屽彲閫�
+
+    public ReviewExportJobStatus() {}
+
+    public ReviewExportJobStatus(String jobId, Status status, String url, String message, Integer progress) {
+        this.jobId = jobId;
+        this.status = status;
+        this.url = url;
+        this.message = message;
+        this.progress = progress;
+    }
+
+    public String getJobId() { return jobId; }
+    public void setJobId(String jobId) { this.jobId = jobId; }
+
+    public Status getStatus() { return status; }
+    public void setStatus(Status status) { this.status = status; }
+
+    public String getUrl() { return url; }
+    public void setUrl(String url) { this.url = url; }
+
+    public String getMessage() { return message; }
+    public void setMessage(String message) { this.message = message; }
+
+    public Integer getProgress() { return progress; }
+    public void setProgress(Integer progress) { this.progress = progress; }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/review/dto/response/ReviewExportResponse.java b/backend/src/main/java/com/rongyichuang/review/dto/response/ReviewExportResponse.java
new file mode 100644
index 0000000..c33d828
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/review/dto/response/ReviewExportResponse.java
@@ -0,0 +1,42 @@
+package com.rongyichuang.review.dto.response;
+
+/**
+ * 璇勫瀵煎嚭鍝嶅簲DTO
+ */
+public class ReviewExportResponse {
+    private boolean success;
+    private String url;
+    private String message;
+
+    public ReviewExportResponse() {}
+
+    public ReviewExportResponse(boolean success, String url, String message) {
+        this.success = success;
+        this.url = url;
+        this.message = message;
+    }
+
+    public boolean isSuccess() {
+        return success;
+    }
+
+    public void setSuccess(boolean success) {
+        this.success = success;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/review/resolver/ReviewResolver.java b/backend/src/main/java/com/rongyichuang/review/resolver/ReviewResolver.java
index e8c8503..aa7f6a6 100644
--- a/backend/src/main/java/com/rongyichuang/review/resolver/ReviewResolver.java
+++ b/backend/src/main/java/com/rongyichuang/review/resolver/ReviewResolver.java
@@ -4,13 +4,19 @@
 import com.rongyichuang.review.dto.response.ReviewProjectPageResponse;
 import com.rongyichuang.review.dto.response.ReviewProjectResponse;
 import com.rongyichuang.review.dto.response.ReviewStatisticsResponse;
+import com.rongyichuang.review.dto.response.ReviewExportResponse;
+import com.rongyichuang.review.dto.response.ReviewExportJobStatus;
 import com.rongyichuang.review.service.ReviewService;
+import com.rongyichuang.review.service.ReviewExportService;
+import com.rongyichuang.review.service.ReviewExportJobService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.graphql.data.method.annotation.Argument;
+import org.springframework.graphql.data.method.annotation.MutationMapping;
 import org.springframework.graphql.data.method.annotation.QueryMapping;
 import org.springframework.stereotype.Controller;
+import java.util.List;
 
 /**
  * 璇勫绠$悊GraphQL瑙f瀽鍣�
@@ -25,6 +31,12 @@
 
     @Autowired
     private UserContextUtil userContextUtil;
+
+    @Autowired
+    private ReviewExportService reviewExportService;
+
+    @Autowired
+    private ReviewExportJobService reviewExportJobService;
 
     /**
      * 鏌ヨ鎴戞湭璇勫鐨勯」鐩垪琛�
@@ -94,4 +106,40 @@
         
         return reviewService.getReviewStatistics(currentJudgeId);
     }
+
+    /**
+     * 瀵煎嚭璇勫ZIP锛岃繑鍥炰笅杞介摼鎺�
+     */
+    @MutationMapping
+    public ReviewExportResponse exportReviewZip(@Argument Long activityId, @Argument List<Long> stageIds) {
+        log.info("瀵煎嚭璇勫ZIP锛宎ctivityId: {}, stageIds: {}", activityId, stageIds);
+        // 鏉冮檺鏍¢獙锛氫粎鍛樺伐锛堢鐞嗗憳/涓诲姙鏂癸級鍙墽琛屽鍑�
+        if (!userContextUtil.isCurrentUserEmployee()) {
+            log.warn("瀵煎嚭璇勫ZIP琚嫆缁濓紝褰撳墠鐢ㄦ埛鏃犲憳宸ユ潈闄愶紝activityId: {}", activityId);
+            return new ReviewExportResponse(false, null, "褰撳墠鐢ㄦ埛鏃犳潈闄愬鍑鸿瘎瀹℃暟鎹�");
+        }
+
+        return reviewExportService.exportReviewZip(activityId, stageIds);
+    }
+
+    /**
+     * 寮傛瀵煎嚭锛氬惎鍔ㄨ瘎瀹″鍑轰换鍔★紝杩斿洖浠诲姟ID
+     */
+    @MutationMapping
+    public String startReviewExportJob(@Argument Long activityId, @Argument List<Long> stageIds) {
+        log.info("鍚姩璇勫瀵煎嚭浠诲姟锛宎ctivityId: {}, stageIds: {}", activityId, stageIds);
+        if (!userContextUtil.isCurrentUserEmployee()) {
+            log.warn("鍚姩璇勫瀵煎嚭浠诲姟琚嫆缁濓紝褰撳墠鐢ㄦ埛鏃犲憳宸ユ潈闄愶紝activityId: {}", activityId);
+            return null;
+        }
+        return reviewExportJobService.startJob(activityId, stageIds);
+    }
+
+    /**
+     * 鏌ヨ瀵煎嚭浠诲姟鐘舵��
+     */
+    @QueryMapping
+    public ReviewExportJobStatus getReviewExportJobStatus(@Argument String jobId) {
+        return reviewExportJobService.getStatus(jobId);
+    }
 }
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/review/service/ReviewExportJobService.java b/backend/src/main/java/com/rongyichuang/review/service/ReviewExportJobService.java
new file mode 100644
index 0000000..38f6562
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/review/service/ReviewExportJobService.java
@@ -0,0 +1,66 @@
+package com.rongyichuang.review.service;
+
+import com.rongyichuang.review.dto.response.ReviewExportJobStatus;
+import com.rongyichuang.review.dto.response.ReviewExportResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+/**
+ * 寮傛璇勫瀵煎嚭浠诲姟鏈嶅姟
+ */
+@Service
+@Slf4j
+public class ReviewExportJobService {
+
+    private final ReviewExportService reviewExportService;
+
+    private final ExecutorService executor = Executors.newFixedThreadPool(Math.max(2, Runtime.getRuntime().availableProcessors() / 2));
+
+    private final ConcurrentHashMap<String, ReviewExportJobStatus> jobStatusMap = new ConcurrentHashMap<>();
+
+    public ReviewExportJobService(ReviewExportService reviewExportService) {
+        this.reviewExportService = reviewExportService;
+    }
+
+    /**
+     * 鍚姩瀵煎嚭浠诲姟
+     */
+    public String startJob(Long activityId, List<Long> stageIds) {
+        String jobId = UUID.randomUUID().toString();
+        ReviewExportJobStatus init = new ReviewExportJobStatus(jobId, ReviewExportJobStatus.Status.PENDING, null, null, 0);
+        jobStatusMap.put(jobId, init);
+
+        executor.submit(() -> {
+            ReviewExportJobStatus running = new ReviewExportJobStatus(jobId, ReviewExportJobStatus.Status.RUNNING, null, null, 10);
+            jobStatusMap.put(jobId, running);
+            try {
+                ReviewExportResponse res = reviewExportService.exportReviewZip(activityId, stageIds);
+                if (res != null && res.isSuccess()) {
+                    ReviewExportJobStatus done = new ReviewExportJobStatus(jobId, ReviewExportJobStatus.Status.SUCCEEDED, res.getUrl(), res.getMessage(), 100);
+                    jobStatusMap.put(jobId, done);
+                } else {
+                    String msg = res != null ? res.getMessage() : "瀵煎嚭澶辫触";
+                    ReviewExportJobStatus failed = new ReviewExportJobStatus(jobId, ReviewExportJobStatus.Status.FAILED, null, msg, 100);
+                    jobStatusMap.put(jobId, failed);
+                }
+            } catch (Exception e) {
+                log.error("瀵煎嚭浠诲姟鎵ц澶辫触, jobId: {}", jobId, e);
+                ReviewExportJobStatus failed = new ReviewExportJobStatus(jobId, ReviewExportJobStatus.Status.FAILED, null, e.getMessage(), 100);
+                jobStatusMap.put(jobId, failed);
+            }
+        });
+
+        return jobId;
+    }
+
+    /**
+     * 鏌ヨ浠诲姟鐘舵��
+     */
+    public ReviewExportJobStatus getStatus(String jobId) {
+        return jobStatusMap.get(jobId);
+    }
+
+}
\ No newline at end of file
diff --git a/backend/src/main/java/com/rongyichuang/review/service/ReviewExportService.java b/backend/src/main/java/com/rongyichuang/review/service/ReviewExportService.java
new file mode 100644
index 0000000..edad964
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/review/service/ReviewExportService.java
@@ -0,0 +1,432 @@
+package com.rongyichuang.review.service;
+
+import com.rongyichuang.activity.entity.Activity;
+import com.rongyichuang.activity.repository.ActivityRepository;
+import com.rongyichuang.player.repository.ActivityPlayerRepository;
+import com.rongyichuang.review.dto.response.ReviewExportResponse;
+import com.rongyichuang.judge.service.CosService;
+import com.rongyichuang.player.entity.ActivityPlayer;
+import com.rongyichuang.activity.repository.ActivityPlayerRatingRepository;
+import com.rongyichuang.activity.repository.ActivityPlayerRatingItemRepository;
+import com.rongyichuang.rating.repository.RatingItemRepository;
+import com.rongyichuang.rating.entity.RatingItem;
+import com.rongyichuang.judge.repository.JudgeRepository;
+import com.rongyichuang.judge.entity.Judge;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.apache.poi.xwpf.usermodel.XWPFRun;
+import org.apache.poi.xwpf.usermodel.XWPFTable;
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
+import org.apache.poi.xwpf.usermodel.XWPFTableCell;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGrid;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGridCol;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblBorders;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder;
+import java.math.BigInteger;
+
+@Service
+public class ReviewExportService {
+    private static final Logger log = LoggerFactory.getLogger(ReviewExportService.class);
+
+    private final ActivityRepository activityRepository;
+    private final ActivityPlayerRepository activityPlayerRepository;
+    private final ActivityPlayerRatingRepository activityPlayerRatingRepository;
+    private final ActivityPlayerRatingItemRepository activityPlayerRatingItemRepository;
+    private final RatingItemRepository ratingItemRepository;
+    private final JudgeRepository judgeRepository;
+    private final CosService cosService;
+
+    public ReviewExportService(ActivityRepository activityRepository,
+                               ActivityPlayerRepository activityPlayerRepository,
+                               ActivityPlayerRatingRepository activityPlayerRatingRepository,
+                               ActivityPlayerRatingItemRepository activityPlayerRatingItemRepository,
+                               RatingItemRepository ratingItemRepository,
+                               JudgeRepository judgeRepository,
+                               CosService cosService) {
+        this.activityRepository = activityRepository;
+        this.activityPlayerRepository = activityPlayerRepository;
+        this.activityPlayerRatingRepository = activityPlayerRatingRepository;
+        this.activityPlayerRatingItemRepository = activityPlayerRatingItemRepository;
+        this.ratingItemRepository = ratingItemRepository;
+        this.judgeRepository = judgeRepository;
+        this.cosService = cosService;
+    }
+
+    /**
+     * 瀵煎嚭璇勫缁撴灉ZIP锛堝崰浣嶅疄鐜帮細鐢熸垚绠�鍗曠殑README姹囨�诲苟涓婁紶锛岃繑鍥濽RL锛�
+     */
+    public ReviewExportResponse exportReviewZip(Long activityId, List<Long> stageIds) {
+        try {
+            // 1) 鏍¢獙娲诲姩
+            Optional<Activity> activityOpt = activityRepository.findById(activityId);
+            if (activityOpt.isEmpty()) {
+                return new ReviewExportResponse(false, null, "娲诲姩涓嶅瓨鍦�: " + activityId);
+            }
+            Activity activity = activityOpt.get();
+
+            // 2) 缁勭粐瀵煎嚭鏂囦欢鍚�
+            // 浠呬繚鐣欏瓧姣嶃�佹暟瀛椼�佽繛瀛楃锛屼互鍙婃墍鏈塙nicode瀛楁瘝瀛楃锛堟兜鐩栦腑鏂囩瓑锛夛紱鍏朵粬鏇挎崲涓轰笅鍒掔嚎
+            String safeName = activity.getName() != null ? activity.getName().replaceAll("[^\\p{L}\\p{N}-]", "_") : "activity";
+            String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+            String fileName = String.format("review-%s-%s.zip", safeName, ts);
+
+            // 3) 鏋勫缓涓存椂ZIP
+            File zipFile = new File(System.getProperty("java.io.tmpdir"), "ryc-export-" + UUID.randomUUID() + ".zip");
+            try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
+                // README锛堥伩鍏嶅湪姝ゅ鍏抽棴ZipOutputStream锛�
+                ZipEntry readme = new ZipEntry("README.txt");
+                zos.putNextEntry(readme);
+                StringBuilder readmeSb = new StringBuilder();
+                readmeSb.append("钃夋槗鍒涜瘎瀹″鍑篭n");
+                readmeSb.append("娲诲姩: ").append(activity.getName()).append(" (ID=").append(activity.getId()).append(")\n");
+                readmeSb.append("瀵煎嚭鏃堕棿: ").append(LocalDateTime.now()).append("\n");
+                if (stageIds != null && !stageIds.isEmpty()) {
+                    readmeSb.append("闃舵ID: ").append(stageIds).append("\n");
+                    // 绠�瑕佺粺璁℃瘡涓樁娈垫姤鍚嶄汉鏁帮紙鍗犱綅缁熻锛�
+                    for (Long stageId : stageIds) {
+                        int playerCount = activityPlayerRepository.findByStageId(stageId).size();
+                        readmeSb.append("闃舵 ").append(stageId).append(" 鎶ュ悕浜烘暟: ").append(playerCount).append("\n");
+                    }
+                } else {
+                    // 涓绘椿鍔ㄦ姤鍚嶄汉鏁帮紙涓嶅尯鍒嗛樁娈碉級
+                    int playerCount = activityPlayerRepository.findByActivityId(activityId).size();
+                    readmeSb.append("娲诲姩鎶ュ悕浜烘暟: ").append(playerCount).append("\n");
+                }
+                byte[] readmeBytes = readmeSb.toString().getBytes(StandardCharsets.UTF_8);
+                zos.write(readmeBytes);
+                zos.closeEntry();
+
+                // 鐢熸垚姣忎釜閫夋墜鐨凞OCX璇勫垎琛紝鎵撳寘鍒癦IP涓紙鍩虹瀹炵幇锛�
+                if (stageIds != null && !stageIds.isEmpty()) {
+                    for (Long stageId : stageIds) {
+                        Activity stage = activityRepository.findById(stageId).orElse(null);
+                        String stageName = stage != null ? stage.getName() : ("闃舵" + stageId);
+                        List<ActivityPlayer> players = activityPlayerRepository.findByStageIdAndStateWithPlayerOrderByCreateTimeDesc(stageId, 1);
+                        for (ActivityPlayer ap : players) {
+                            addPlayerDocToZip(zos, activity, stageName, ap);
+                        }
+                    }
+                } else {
+                    List<ActivityPlayer> players = activityPlayerRepository.findByActivityIdWithPlayerAndRegion(activityId);
+                    for (ActivityPlayer ap : players) {
+                        String stageName = ap.getStageId() != null ? activityRepository.findById(ap.getStageId()).map(Activity::getName).orElse("闃舵" + ap.getStageId()) : "涓绘椿鍔�";
+                        addPlayerDocToZip(zos, activity, stageName, ap);
+                    }
+                }
+            }
+
+            // 4) 涓婁紶鍒癈OS
+            String key = cosService.uploadLocalFile(zipFile, fileName);
+            String url = cosService.getFileUrl(key);
+            log.info("璇勫瀵煎嚭涓婁紶瀹屾垚锛孋OS Key: {}, URL: {}", key, url);
+
+            // 5) 鍐欏叆 t_activity.review_export_url
+            if (stageIds != null && !stageIds.isEmpty()) {
+                for (Long stageId : stageIds) {
+                    Activity stage = activityRepository.findById(stageId).orElse(null);
+                    if (stage != null) {
+                        stage.setReviewExportUrl(url);
+                        activityRepository.save(stage);
+                    }
+                }
+            } else {
+                activity.setReviewExportUrl(url);
+                activityRepository.save(activity);
+            }
+
+            // 鍒犻櫎涓存椂鏂囦欢
+            try { zipFile.delete(); } catch (Exception ignore) {}
+
+            return new ReviewExportResponse(true, url, "瀵煎嚭鎴愬姛");
+        } catch (Exception ex) {
+            log.error("瀵煎嚭璇勫ZIP澶辫触", ex);
+            return new ReviewExportResponse(false, null, "瀵煎嚭澶辫触: " + ex.getMessage());
+        }
+    }
+
+    private void addPlayerDocToZip(ZipOutputStream zos, Activity activity, String stageName, ActivityPlayer ap) throws Exception {
+        String projectName = ap.getProjectName() != null ? ap.getProjectName() : "鏈懡鍚嶉」鐩�";
+        String playerName = ap.getPlayer() != null && ap.getPlayer().getName() != null ? ap.getPlayer().getName() : ("閫夋墜" + ap.getPlayerId());
+
+        // 鐢熸垚DOCX鍐呭
+        XWPFDocument doc = new XWPFDocument();
+        // 璁剧疆椤甸潰涓篈4骞跺簲鐢ㄥ悎鐞嗛〉杈硅窛锛屼繚璇佹墦鍗扮増寮�
+        applyPageSettings(doc);
+        // 鏍囬
+        XWPFParagraph title = doc.createParagraph();
+        title.setAlignment(ParagraphAlignment.CENTER);
+        XWPFRun tr = title.createRun();
+        tr.setText("璇勫璇勫垎琛�");
+        tr.setBold(true);
+        tr.setFontSize(18);
+
+        // 鍩烘湰淇℃伅
+        XWPFParagraph p1 = doc.createParagraph();
+        XWPFRun r1 = p1.createRun();
+        r1.setText("娲诲姩锛�" + safeText(activity.getName()) + "    闃舵锛�" + safeText(stageName));
+
+        XWPFParagraph p2 = doc.createParagraph();
+        XWPFRun r2 = p2.createRun();
+        r2.setText("椤圭洰锛�" + safeText(projectName) + "    閫夋墜锛�" + safeText(playerName));
+
+        // 鏌ヨ鎵�鏈夎瘎濮旇瘎鍒�
+        List<com.rongyichuang.activity.entity.ActivityPlayerRating> ratings = activityPlayerRatingRepository.findByActivityPlayerId(ap.getId());
+        if (ratings == null || ratings.isEmpty()) {
+            // 鏃犺瘎鍒嗘椂锛屼粛鐒剁敓鎴愯瘎鍒嗛」妯℃澘琛ㄦ牸锛屼究浜庢墦鍗版垨绾夸笅璇勫垎
+            XWPFParagraph pEmpty = doc.createParagraph();
+            XWPFRun re = pEmpty.createRun();
+            re.setText("灏氭棤璇勫垎锛堜互涓嬩负璇勫垎椤规ā鏉匡級");
+            re.setItalic(true);
+
+            // 浣跨敤娲诲姩鐨勮瘎鍒嗘柟妗堢敓鎴愮┖琛ㄦ牸锛堣缃〃鏍煎搴﹀拰鍒楀锛岄伩鍏嶈〃鏍兼尋鍘嬶級
+            XWPFTable table = doc.createTable();
+            applyTableLayout(table, new int[]{6072, 1500, 1500});
+            XWPFTableRow header = table.getRow(0);
+            ensureCells(header, 3);
+            setCellText(header.getCell(0), "璇勫垎椤�");
+            setCellText(header.getCell(1), "寰楀垎");
+            setCellText(header.getCell(2), "婊″垎");
+
+            // 浼樺厛浣跨敤闃舵鐨勮瘎鍒嗘柟妗堬紱濡傛灉閫夋墜娌℃湁闃舵鎴栭樁娈典笉瀛樺湪锛屽垯鍥為��鍒颁富娲诲姩璇勫垎鏂规
+            Long schemeIdToUse = null;
+            if (ap.getStageId() != null) {
+                try {
+                    Activity stageEntity = activityRepository.findById(ap.getStageId()).orElse(null);
+                    if (stageEntity != null && stageEntity.getRatingSchemeId() != null) {
+                        schemeIdToUse = stageEntity.getRatingSchemeId();
+                    }
+                } catch (Exception ignore) {}
+            }
+            if (schemeIdToUse == null) {
+                schemeIdToUse = activity.getRatingSchemeId();
+            }
+
+            List<RatingItem> items = schemeIdToUse != null
+                ? ratingItemRepository.findBySchemeIdOrderByOrderNo(schemeIdToUse)
+                : new ArrayList<>();
+            for (RatingItem item : items) {
+                XWPFTableRow row = table.createRow();
+                ensureCells(row, 3);
+                setCellText(row.getCell(0), safeText(item.getName()));
+                setCellText(row.getCell(1), "-");
+                setCellText(row.getCell(2), item.getMaxScore() != null ? String.valueOf(item.getMaxScore()) : "-");
+            }
+        } else {
+            for (com.rongyichuang.activity.entity.ActivityPlayerRating rating : ratings) {
+                XWPFParagraph judgeTitle = doc.createParagraph();
+                XWPFRun jr = judgeTitle.createRun();
+                String judgeName = rating.getJudgeId() != null ? judgeRepository.findById(rating.getJudgeId()).map(Judge::getName).orElse("璇勫" + rating.getJudgeId()) : "鏈煡璇勫";
+                jr.setText("璇勫锛�" + safeText(judgeName) + "    鎬诲垎锛�" + (rating.getTotalScore() != null ? rating.getTotalScore() : "-") );
+                jr.setBold(true);
+
+                // 鏋勫缓璇勫垎椤硅〃鏍�
+                XWPFTable table = doc.createTable();
+                applyTableLayout(table, new int[]{6072, 1500, 1500});
+                XWPFTableRow header = table.getRow(0);
+                ensureCells(header, 3);
+                setCellText(header.getCell(0), "璇勫垎椤�");
+                setCellText(header.getCell(1), "寰楀垎");
+                setCellText(header.getCell(2), "婊″垎");
+
+                List<RatingItem> items = rating.getRatingSchemeId() != null ? ratingItemRepository.findBySchemeIdOrderByOrderNo(rating.getRatingSchemeId()) : new ArrayList<>();
+                // 鏌ヨ璇勫垎椤瑰緱鍒�
+                List<com.rongyichuang.activity.entity.ActivityPlayerRatingItem> ratingItems = activityPlayerRatingItemRepository.findByActivityPlayerRatingId(rating.getId());
+                Map<Long, java.math.BigDecimal> scoreMap = new HashMap<>();
+                for (com.rongyichuang.activity.entity.ActivityPlayerRatingItem ri : ratingItems) {
+                    scoreMap.put(ri.getRatingItemId(), ri.getScore());
+                }
+                for (RatingItem item : items) {
+                    XWPFTableRow row = table.createRow();
+                    ensureCells(row, 3);
+                    setCellText(row.getCell(0), safeText(item.getName()));
+                    java.math.BigDecimal s = scoreMap.get(item.getId());
+                    setCellText(row.getCell(1), s != null ? s.toPlainString() : "-");
+                    setCellText(row.getCell(2), item.getMaxScore() != null ? String.valueOf(item.getMaxScore()) : "-");
+                }
+            }
+        }
+
+        // 鍦ㄦ枃妗e簳閮ㄦ坊鍔犱笓瀹惰瘎瀹$瀛楀尯鍩燂紙鍙傝�冩ā鏉挎牸寮忥級
+        addSignatureSection(doc);
+
+        // 鍐欏叆鍒癦IP
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            doc.write(baos);
+            String fileBase = sanitizeFileName(stageName) + "/" + sanitizeFileName(projectName + "-" + playerName) + ".docx";
+            String entryName = "players/" + fileBase;
+            ZipEntry entry = new ZipEntry(entryName);
+            zos.putNextEntry(entry);
+            zos.write(baos.toByteArray());
+            zos.closeEntry();
+        }
+        doc.close();
+    }
+
+    private String sanitizeFileName(String name) {
+        return name == null ? "unknown" : name.replaceAll("[^\\p{L}\\p{N}-]", "_");
+    }
+
+    private String safeText(String text) {
+        return text == null ? "-" : text;
+    }
+
+    /**
+     * 璁剧疆琛ㄦ牸鐨勬�诲搴﹀拰鍒楀锛岄伩鍏嶅嚭鐜板垪瀹借繃绐勫鑷村唴瀹规尋鍦ㄤ竴璧�
+     * @param table  琛ㄦ牸
+     * @param colWidthsTwips 姣忓垪瀹藉害锛堝崟浣嶏細twips锛夛紝绀轰緥锛歿6072, 1500, 1500}
+     */
+    private void applyTableLayout(XWPFTable table, int[] colWidthsTwips) {
+        // 璁剧疆琛ㄦ牸鎬诲搴︼紙椤甸潰鏍囧噯瀹藉害绾� 9072 twips锛�
+        CTTbl ctTbl = table.getCTTbl();
+        CTTblPr tblPr = ctTbl.getTblPr() == null ? ctTbl.addNewTblPr() : ctTbl.getTblPr();
+        CTTblWidth tblW = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
+        tblW.setType(STTblWidth.DXA);
+        int total = 0;
+        for (int w : colWidthsTwips) total += w;
+        tblW.setW(BigInteger.valueOf(total));
+
+        // 璁剧疆鍒楃綉鏍硷紙鍒楀锛�
+        CTTblGrid grid = ctTbl.getTblGrid() == null ? ctTbl.addNewTblGrid() : ctTbl.getTblGrid();
+        // 娓呯悊宸叉湁鍒楀畾涔夛紙濡傛灉鏈夛級
+        while (grid.sizeOfGridColArray() > 0) {
+            grid.removeGridCol(0);
+        }
+        for (int w : colWidthsTwips) {
+            CTTblGridCol col = grid.addNewGridCol();
+            col.setW(BigInteger.valueOf(w));
+        }
+
+        // 璁剧疆琛ㄦ牸杈规锛屼繚璇佹墦鍗版晥鏋滄竻鏅�
+        CTTblBorders borders = tblPr.isSetTblBorders() ? tblPr.getTblBorders() : tblPr.addNewTblBorders();
+        CTBorder border = borders.isSetInsideH() ? borders.getInsideH() : borders.addNewInsideH();
+        border.setVal(STBorder.SINGLE);
+        border.setSz(BigInteger.valueOf(8)); // 8鍏垎涔嬩竴鐐� = 1pt
+        border.setColor("000000");
+        border = borders.isSetInsideV() ? borders.getInsideV() : borders.addNewInsideV();
+        border.setVal(STBorder.SINGLE);
+        border.setSz(BigInteger.valueOf(8));
+        border.setColor("000000");
+        border = borders.isSetTop() ? borders.getTop() : borders.addNewTop();
+        border.setVal(STBorder.SINGLE);
+        border.setSz(BigInteger.valueOf(8));
+        border.setColor("000000");
+        border = borders.isSetBottom() ? borders.getBottom() : borders.addNewBottom();
+        border.setVal(STBorder.SINGLE);
+        border.setSz(BigInteger.valueOf(8));
+        border.setColor("000000");
+        border = borders.isSetLeft() ? borders.getLeft() : borders.addNewLeft();
+        border.setVal(STBorder.SINGLE);
+        border.setSz(BigInteger.valueOf(8));
+        border.setColor("000000");
+        // 淇鍙宠竟妗嗘湭璁剧疆绮楃粏鐨勯棶棰橈紝纭繚鍥涘懆杈规涓�鑷�
+        border = borders.isSetRight() ? borders.getRight() : borders.addNewRight();
+        border.setVal(STBorder.SINGLE);
+        border.setSz(BigInteger.valueOf(8));
+        border.setColor("000000");
+    }
+
+    /**
+     * 纭繚涓�琛屾湁鎸囧畾鏁伴噺鐨勫崟鍏冩牸
+     */
+    private void ensureCells(XWPFTableRow row, int cellCount) {
+        int existing = row.getTableCells().size();
+        for (int i = existing; i < cellCount; i++) {
+            row.addNewTableCell();
+        }
+    }
+
+    /**
+     * 璁剧疆鍗曞厓鏍兼枃鏈苟涓鸿鍗曞厓鏍艰缃搴︼紙涓庣綉鏍煎尮閰嶏級
+     */
+    private void setCellText(XWPFTableCell cell, String text) {
+        // 鐩存帴璁剧疆鏂囨湰
+        try {
+            // 娓呯┖榛樿娈佃惤锛岄伩鍏嶉噸澶嶏紙鏌愪簺鎯呭喌涓嬪崟鍏冩牸鍙兘娌℃湁榛樿娈佃惤锛�
+            if (cell.getParagraphs().size() > 0) {
+                cell.removeParagraph(0);
+            }
+        } catch (Exception ignore) {}
+        XWPFParagraph p = cell.addParagraph();
+        p.setAlignment(ParagraphAlignment.LEFT);
+        XWPFRun run = p.createRun();
+        run.setFontSize(12);
+        // 浣跨敤涓枃瀛椾綋锛屼繚璇佷腑鏂囨樉绀烘晥鏋滄洿鎺ヨ繎妯℃澘
+        try {
+            run.setFontFamily("瀹嬩綋");
+        } catch (Exception ignore) {}
+        run.setText(text);
+
+        // 涓哄崟鍏冩牸璁剧疆瀹藉害锛圖XA锛変互闃插湪鏌愪簺Word鐗堟湰涓湭搴旂敤琛ㄦ牸缃戞牸瀹藉害
+        CTTcPr tcPr = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();
+        CTTblWidth w = tcPr.isSetTcW() ? tcPr.getTcW() : tcPr.addNewTcW();
+        w.setType(STTblWidth.DXA);
+        // 瀹藉害鍊肩敱鎵�鍦ㄥ垪鍐冲畾锛岃繖閲屼笉閲嶅璁剧疆鍏蜂綋鏁板�硷紝閬垮厤涓庤〃鏍肩綉鏍煎啿绐�
+    }
+
+    /**
+     * 娣诲姞涓撳璇勫绛惧瓧鍖哄煙锛堜袱鍒楋細绛惧瓧銆佹棩鏈燂級
+     */
+    private void addSignatureSection(XWPFDocument doc) {
+        // 鐣欏嚭涓�瀹氱殑涓婅竟璺�
+        XWPFParagraph spacer = doc.createParagraph();
+        spacer.setSpacingBefore(200);
+
+        XWPFTable signTable = doc.createTable(1, 2);
+        applyTableLayout(signTable, new int[]{6500, 2572});
+        XWPFTableRow row = signTable.getRow(0);
+
+        ensureCells(row, 2);
+        setCellText(row.getCell(0), "涓撳璇勫绛惧瓧锛歘______________");
+        setCellText(row.getCell(1), "绛惧瓧鏃ユ湡锛歘_________");
+    }
+
+    /**
+     * 璁剧疆椤甸潰澶у皬涓庨〉杈硅窛锛圓4锛岄粯璁よ竟璺濈害1鑻卞锛�
+     */
+    private void applyPageSettings(XWPFDocument doc) {
+        try {
+            // A4 灏哄锛氬 11907 twips锛�21cm锛夛紝楂� 16840 twips锛�29.7cm锛�
+            var body = doc.getDocument().getBody();
+            var sectPr = body.isSetSectPr() ? body.getSectPr() : body.addNewSectPr();
+            var pgSz = sectPr.isSetPgSz() ? sectPr.getPgSz() : sectPr.addNewPgSz();
+            pgSz.setW(BigInteger.valueOf(11907));
+            pgSz.setH(BigInteger.valueOf(16840));
+
+            // 椤佃竟璺濓細绾� 1 鑻卞锛�1440 twips锛夛紝鍙牴鎹渶瑕佸井璋�
+            var pgMar = sectPr.isSetPgMar() ? sectPr.getPgMar() : sectPr.addNewPgMar();
+            pgMar.setLeft(BigInteger.valueOf(1440));
+            pgMar.setRight(BigInteger.valueOf(1440));
+            pgMar.setTop(BigInteger.valueOf(1440));
+            pgMar.setBottom(BigInteger.valueOf(1440));
+        } catch (Exception ignore) {
+            // 鍏煎鎬ц�冭檻锛氫笉褰卞搷鏂囨。鐢熸垚
+        }
+    }
+}
\ No newline at end of file
diff --git a/backend/src/main/resources/graphql/activity.graphqls b/backend/src/main/resources/graphql/activity.graphqls
index ba530a4..e497252 100644
--- a/backend/src/main/resources/graphql/activity.graphqls
+++ b/backend/src/main/resources/graphql/activity.graphqls
@@ -70,6 +70,7 @@
     createTime: String
 
     updateTime: String
+    reviewExportUrl: String
 
     coverImage: MediaResponse
 
diff --git a/backend/src/main/resources/graphql/review.graphqls b/backend/src/main/resources/graphql/review.graphqls
index 6c3e122..818e2d5 100644
--- a/backend/src/main/resources/graphql/review.graphqls
+++ b/backend/src/main/resources/graphql/review.graphqls
@@ -13,6 +13,41 @@
     
     # 鑾峰彇璇勫缁熻鏁版嵁
     reviewStatistics: ReviewStatisticsResponse!
+
+    # 鏌ヨ璇勫瀵煎嚭浠诲姟鐘舵��
+    getReviewExportJobStatus(jobId: String!): ReviewExportJobStatus
+}
+
+# 鎵╁睍鍙樻洿绫诲瀷锛氳瘎瀹″鍑�
+extend type Mutation {
+    # 瀵煎嚭鎸囧畾姣旇禌鎴栭樁娈电殑璇勫缁撴灉涓篫IP骞惰繑鍥炰笅杞介摼鎺�
+    exportReviewZip(activityId: ID!, stageIds: [ID]): ReviewExportResponse!
+
+    # 鍚姩寮傛璇勫瀵煎嚭浠诲姟锛岃繑鍥炰换鍔D
+    startReviewExportJob(activityId: ID!, stageIds: [ID]): String!
+}
+
+# 璇勫瀵煎嚭鍝嶅簲
+type ReviewExportResponse {
+    success: Boolean!
+    url: String
+    message: String
+}
+
+# 璇勫瀵煎嚭浠诲姟鐘舵��
+type ReviewExportJobStatus {
+    jobId: String!
+    status: ReviewExportJobStatusEnum!
+    url: String
+    message: String
+    progress: Int
+}
+
+enum ReviewExportJobStatusEnum {
+    PENDING
+    RUNNING
+    SUCCEEDED
+    FAILED
 }
 
 # 璇勫椤圭洰鍒嗛〉鍝嶅簲绫诲瀷
diff --git a/backend/src/test/java/com/rongyichuang/review/DocxLayoutTest.java b/backend/src/test/java/com/rongyichuang/review/DocxLayoutTest.java
new file mode 100644
index 0000000..0895adc
--- /dev/null
+++ b/backend/src/test/java/com/rongyichuang/review/DocxLayoutTest.java
@@ -0,0 +1,114 @@
+package com.rongyichuang.review;
+
+import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.apache.poi.xwpf.usermodel.XWPFRun;
+import org.apache.poi.xwpf.usermodel.XWPFTable;
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
+import org.apache.poi.xwpf.usermodel.XWPFTableCell;
+import org.junit.jupiter.api.Test;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGrid;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGridCol;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.math.BigInteger;
+
+/**
+ * 蹇�熼獙璇丏OCX琛ㄦ牸瀹藉害涓庣瀛楀尯鍩熷竷灞�
+ */
+public class DocxLayoutTest {
+
+    private void applyTableLayout(XWPFTable table, int[] colWidthsTwips) {
+        CTTbl ctTbl = table.getCTTbl();
+        CTTblPr tblPr = ctTbl.getTblPr() == null ? ctTbl.addNewTblPr() : ctTbl.getTblPr();
+        CTTblWidth tblW = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
+        tblW.setType(STTblWidth.DXA);
+        int total = 0;
+        for (int w : colWidthsTwips) total += w;
+        tblW.setW(BigInteger.valueOf(total));
+
+        CTTblGrid grid = ctTbl.getTblGrid() == null ? ctTbl.addNewTblGrid() : ctTbl.getTblGrid();
+        while (grid.sizeOfGridColArray() > 0) {
+            grid.removeGridCol(0);
+        }
+        for (int w : colWidthsTwips) {
+            CTTblGridCol col = grid.addNewGridCol();
+            col.setW(BigInteger.valueOf(w));
+        }
+    }
+
+    private void ensureCells(XWPFTableRow row, int cellCount) {
+        int existing = row.getTableCells().size();
+        for (int i = existing; i < cellCount; i++) {
+            row.addNewTableCell();
+        }
+    }
+
+    private void setCellText(XWPFTableCell cell, String text) {
+        while (cell.getParagraphs().size() > 0) {
+            cell.removeParagraph(0);
+        }
+        XWPFParagraph p = cell.addParagraph();
+        p.setAlignment(ParagraphAlignment.LEFT);
+        XWPFRun run = p.createRun();
+        run.setFontSize(12);
+        run.setText(text);
+
+        CTTcPr tcPr = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();
+        CTTblWidth w = tcPr.isSetTcW() ? tcPr.getTcW() : tcPr.addNewTcW();
+        w.setType(STTblWidth.DXA);
+    }
+
+    @Test
+    public void generateSampleDoc() throws Exception {
+        XWPFDocument doc = new XWPFDocument();
+
+        XWPFParagraph title = doc.createParagraph();
+        title.setAlignment(ParagraphAlignment.CENTER);
+        XWPFRun tr = title.createRun();
+        tr.setText("璇勫璇勫垎琛紙甯冨眬楠岃瘉锛�");
+        tr.setBold(true);
+        tr.setFontSize(18);
+
+        XWPFTable table = doc.createTable();
+        applyTableLayout(table, new int[]{6072, 1500, 1500});
+        XWPFTableRow header = table.getRow(0);
+        ensureCells(header, 3);
+        setCellText(header.getCell(0), "璇勫垎椤�");
+        setCellText(header.getCell(1), "寰楀垎");
+        setCellText(header.getCell(2), "婊″垎");
+
+        for (int i = 1; i <= 5; i++) {
+            XWPFTableRow row = table.createRow();
+            ensureCells(row, 3);
+            setCellText(row.getCell(0), "绀轰緥椤圭洰" + i);
+            setCellText(row.getCell(1), String.valueOf(10 * i));
+            setCellText(row.getCell(2), "100");
+        }
+
+        // 绛惧瓧鍖�
+        XWPFParagraph spacer = doc.createParagraph();
+        spacer.setSpacingBefore(200);
+
+        XWPFTable signTable = doc.createTable(1, 2);
+        applyTableLayout(signTable, new int[]{6500, 2572});
+        XWPFTableRow row = signTable.getRow(0);
+        ensureCells(row, 2);
+        setCellText(row.getCell(0), "涓撳璇勫绛惧瓧锛歘______________");
+        setCellText(row.getCell(1), "绛惧瓧鏃ユ湡锛歘_________");
+
+        File out = new File("d:/code/new-ryc/tmp/docx-layout-sample.docx");
+        out.getParentFile().mkdirs();
+        try (FileOutputStream fos = new FileOutputStream(out)) {
+            doc.write(fos);
+        }
+        doc.close();
+    }
+}
\ No newline at end of file
diff --git a/check-all-education-data.js b/check-all-education-data.js
deleted file mode 100644
index 6b8d0ce..0000000
--- a/check-all-education-data.js
+++ /dev/null
@@ -1,131 +0,0 @@
-// 妫�鏌ユ墍鏈夊弬璧涗汉鐨勫鍘嗘暟鎹�
-const axios = require('axios');
-
-async function checkAllEducationData() {
-  try {
-    console.log('=== 妫�鏌ユ墍鏈夊弬璧涗汉鐨勫鍘嗘暟鎹� ===');
-    
-    // 鏌ヨ鎵�鏈夊弬璧涗汉
-    const listQuery = `
-      query {
-        activityPlayerApplications(page: 1, size: 50) {
-          content {
-            id
-            playerName
-            projectName
-          }
-          totalElements
-        }
-      }
-    `;
-
-    const listResponse = await axios.post('http://localhost:8080/api/graphql', {
-      query: listQuery
-    }, {
-      headers: {
-        'Content-Type': 'application/json'
-      }
-    });
-
-    if (listResponse.data.errors) {
-      console.log('鏌ヨ閿欒:', listResponse.data.errors);
-      return;
-    }
-
-    const applications = listResponse.data.data?.activityPlayerApplications?.content || [];
-    const total = listResponse.data.data?.activityPlayerApplications?.totalElements || 0;
-    
-    console.log(`鎬诲叡鎵惧埌 ${total} 涓弬璧涗汉锛屽紑濮嬫鏌ュ鍘嗘暟鎹�...`);
-
-    let educationStats = {
-      total: 0,
-      hasEducation: 0,
-      noEducation: 0,
-      educationTypes: {}
-    };
-
-    // 妫�鏌ユ瘡涓弬璧涗汉鐨勫鍘�
-    for (let i = 0; i < applications.length; i++) {
-      const app = applications[i];
-      
-      // 鏌ヨ璇︽儏
-      const detailQuery = `
-        query GetActivityPlayerDetail($id: ID!) {
-          activityPlayerDetail(id: $id) {
-            id
-            playerInfo {
-              name
-              education
-            }
-          }
-        }
-      `;
-
-      try {
-        const detailResponse = await axios.post('http://localhost:8080/api/graphql', {
-          query: detailQuery,
-          variables: { id: app.id }
-        }, {
-          headers: {
-            'Content-Type': 'application/json'
-          }
-        });
-
-        if (detailResponse.data.errors) {
-          console.log(`鍙傝禌浜� ${app.id} 鏌ヨ閿欒:`, detailResponse.data.errors);
-          continue;
-        }
-
-        const detail = detailResponse.data.data?.activityPlayerDetail;
-        if (!detail) {
-          console.log(`鍙傝禌浜� ${app.id} 鏈壘鍒拌鎯卄);
-          continue;
-        }
-
-        educationStats.total++;
-        const education = detail.playerInfo.education || '';
-        
-        if (education && education.trim()) {
-          educationStats.hasEducation++;
-          if (educationStats.educationTypes[education]) {
-            educationStats.educationTypes[education]++;
-          } else {
-            educationStats.educationTypes[education] = 1;
-          }
-          
-          console.log(`鉁� 鍙傝禌浜� ${detail.playerInfo.name} (ID: ${app.id}) 鏈夊鍘�: "${education}"`);
-        } else {
-          educationStats.noEducation++;
-          console.log(`鉂� 鍙傝禌浜� ${detail.playerInfo.name} (ID: ${app.id}) 瀛﹀巻涓虹┖`);
-        }
-
-      } catch (error) {
-        console.log(`鍙傝禌浜� ${app.id} 鏌ヨ澶辫触:`, error.message);
-      }
-    }
-
-    console.log('\n=== 瀛﹀巻鏁版嵁缁熻 ===');
-    console.log(`鎬诲弬璧涗汉鏁�: ${educationStats.total}`);
-    console.log(`鏈夊鍘嗘暟鎹�: ${educationStats.hasEducation}`);
-    console.log(`鏃犲鍘嗘暟鎹�: ${educationStats.noEducation}`);
-    
-    if (educationStats.hasEducation > 0) {
-      console.log('\n瀛﹀巻鍒嗗竷:');
-      Object.entries(educationStats.educationTypes).forEach(([education, count]) => {
-        console.log(`  ${education}: ${count} 浜篳);
-      });
-    } else {
-      console.log('\n鉂� 鎵�鏈夊弬璧涗汉閮芥病鏈夊鍘嗘暟鎹紒');
-      console.log('鍙兘鐨勫師鍥�:');
-      console.log('1. 鐢ㄦ埛鍦ㄦ敞鍐屾椂纭疄娌℃湁閫夋嫨瀛﹀巻');
-      console.log('2. 娉ㄥ唽琛ㄥ崟鐨勫鍘嗗瓧娈垫湁闂');
-      console.log('3. 鏁版嵁鎻愪氦鏃跺鍘嗗瓧娈典涪澶�');
-      console.log('4. 鏁版嵁搴撳瓨鍌ㄦ椂鏈夐棶棰�');
-    }
-
-  } catch (error) {
-    console.error('妫�鏌ュけ璐�:', error.message);
-  }
-}
-
-checkAllEducationData();
\ No newline at end of file
diff --git a/check-database-activity-times.js b/check-database-activity-times.js
deleted file mode 100644
index ce45aac..0000000
--- a/check-database-activity-times.js
+++ /dev/null
@@ -1,58 +0,0 @@
-const mysql = require('mysql2/promise');
-
-async function checkDatabaseActivityTimes() {
-    console.log('馃攳 妫�鏌ユ暟鎹簱涓殑姣旇禌鏃堕棿鏁版嵁...\n');
-    
-    const connection = await mysql.createConnection({
-        host: 'localhost',
-        user: 'root',
-        password: 'root',
-        database: 'ryc',
-        port: 3306
-    });
-
-    try {
-        // 鏌ヨActivity琛ㄤ腑鐨勬椂闂村瓧娈�
-        const [activities] = await connection.execute(`
-            SELECT 
-                id, 
-                name, 
-                signup_deadline, 
-                match_time,
-                created_at,
-                updated_at
-            FROM Activity 
-            WHERE id IN (16, 19, 22, 25, 28, 31)
-            ORDER BY id
-        `);
-
-        console.log('馃搳 鏁版嵁搴撲腑鐨勬瘮璧涙椂闂存暟鎹�:');
-        console.log('鈹�'.repeat(80));
-        
-        activities.forEach(activity => {
-            console.log(`馃弳 ID: ${activity.id} - ${activity.name}`);
-            console.log(`   馃搮 signup_deadline: ${activity.signup_deadline}`);
-            console.log(`   馃弫 match_time: ${activity.match_time}`);
-            console.log(`   馃摑 created_at: ${activity.created_at}`);
-            console.log(`   馃攧 updated_at: ${activity.updated_at}`);
-            console.log('鈹�'.repeat(40));
-        });
-
-        // 妫�鏌ユ槸鍚︽湁鍏朵粬鏃堕棿鐩稿叧鐨勫瓧娈�
-        const [columns] = await connection.execute(`
-            SHOW COLUMNS FROM Activity LIKE '%time%' OR SHOW COLUMNS FROM Activity LIKE '%date%'
-        `);
-        
-        console.log('\n馃攳 Activity琛ㄤ腑鎵�鏈夋椂闂寸浉鍏冲瓧娈�:');
-        columns.forEach(col => {
-            console.log(`   ${col.Field}: ${col.Type} (${col.Null === 'YES' ? 'nullable' : 'not null'})`);
-        });
-
-    } catch (error) {
-        console.error('鉂� 鏁版嵁搴撴煡璇㈠け璐�:', error.message);
-    } finally {
-        await connection.end();
-    }
-}
-
-checkDatabaseActivityTimes();
\ No newline at end of file
diff --git a/check-db-gender-education.js b/check-db-gender-education.js
deleted file mode 100644
index 87cd75b..0000000
--- a/check-db-gender-education.js
+++ /dev/null
@@ -1,139 +0,0 @@
-// 鐩存帴鏌ヨ鐗瑰畾鍙傝禌浜虹殑璇︽儏鏁版嵁
-const axios = require('axios');
-
-async function checkPlayerDetail(playerId) {
-  const detailQuery = `
-    query GetActivityPlayerDetail($id: ID!) {
-      activityPlayerDetail(id: $id) {
-        id
-        projectName
-        playerInfo {
-          id
-          name
-          gender
-          education
-          birthday
-        }
-        regionInfo {
-          name
-        }
-      }
-    }
-  `;
-
-  try {
-    const detailResponse = await axios.post('http://localhost:8080/api/graphql', {
-      query: detailQuery,
-      variables: { id: playerId }
-    }, {
-      headers: {
-        'Content-Type': 'application/json'
-      }
-    });
-
-    if (detailResponse.data.errors) {
-      console.log('  璇︽儏鏌ヨ閿欒:', detailResponse.data.errors);
-      return;
-    }
-
-    const detail = detailResponse.data.data?.activityPlayerDetail;
-    if (!detail) {
-      console.log('  鏈壘鍒拌鎯呮暟鎹�');
-      return;
-    }
-
-    console.log('  璇︽儏鏁版嵁:');
-    console.log(`    濮撳悕: ${detail.playerInfo.name}`);
-    console.log(`    鎬у埆 (鍘熷): ${detail.playerInfo.gender} (${typeof detail.playerInfo.gender})`);
-    console.log(`    瀛﹀巻 (鍘熷): "${detail.playerInfo.education}" (${typeof detail.playerInfo.education})`);
-    console.log(`    鐢熸棩: ${detail.playerInfo.birthday}`);
-    console.log(`    鍦板尯: ${detail.regionInfo?.name}`);
-
-    // 娴嬭瘯鎬у埆杞崲閫昏緫
-    let genderText = '鏈~鍐�';
-    if (detail.playerInfo.gender === 0) genderText = '濂�';
-    if (detail.playerInfo.gender === 1) genderText = '鐢�';
-    console.log(`    鎬у埆杞崲缁撴灉: ${genderText}`);
-
-    // 娴嬭瘯瀛﹀巻澶勭悊閫昏緫
-    const education = detail.playerInfo.education || '';
-    console.log(`    瀛﹀巻闀垮害: ${education.length}`);
-    console.log(`    瀛﹀巻澶勭悊缁撴灉: ${education || '鏈~鍐�'}`);
-    
-    // 妫�鏌ラ棶棰�
-    if (detail.playerInfo.gender === 1 && genderText === '濂�') {
-      console.log(`    鉂� 鎬у埆杞崲閿欒: 搴旇鏄敺锛屼絾鏄剧ず涓哄コ`);
-    }
-    if (detail.playerInfo.education && detail.playerInfo.education.trim() && (education || '鏈~鍐�') === '鏈~鍐�') {
-      console.log(`    鉂� 瀛﹀巻鏄剧ず閿欒: 鏈夊鍘嗕絾鏄剧ず涓烘湭濉啓`);
-    }
-    
-    // 妫�鏌ュ綋鍓嶇殑getGenderText鍑芥暟閫昏緫
-    console.log(`    褰撳墠getGenderText閫昏緫娴嬭瘯:`);
-    console.log(`      gender === 0 ? '濂�' : gender === 1 ? '鐢�' : '鏈~鍐�'`);
-    const currentLogic = detail.playerInfo.gender === 0 ? '濂�' : detail.playerInfo.gender === 1 ? '鐢�' : '鏈~鍐�';
-    console.log(`      缁撴灉: ${currentLogic}`);
-
-  } catch (error) {
-    console.log(`  鏌ヨ璇︽儏澶辫触: ${error.message}`);
-  }
-}
-
-async function checkSpecificPlayerData() {
-  try {
-    // 鍏堟煡璇㈠弬璧涗汉鍒楄〃
-    const listQuery = `
-      query {
-        activityPlayerApplications(page: 1, size: 20) {
-          content {
-            id
-            playerName
-            projectName
-            phone
-            state
-          }
-        }
-      }
-    `;
-
-    console.log('=== 鏌ヨ鍙傝禌浜哄垪琛� ===');
-    const listResponse = await axios.post('http://localhost:8080/api/graphql', {
-      query: listQuery
-    }, {
-      headers: {
-        'Content-Type': 'application/json'
-      }
-    });
-
-    if (listResponse.data.errors) {
-      console.log('鍒楄〃鏌ヨ閿欒:', listResponse.data.errors);
-      return;
-    }
-
-    const applications = listResponse.data.data?.activityPlayerApplications?.content || [];
-    
-    if (applications.length === 0) {
-      console.log('娌℃湁鎵惧埌鍙傝禌浜烘暟鎹�');
-      return;
-    }
-
-    console.log(`鎵惧埌 ${applications.length} 涓弬璧涗汉锛屽紑濮嬫煡璇㈣鎯�...`);
-
-    // 鏌ヨ姣忎釜鍙傝禌浜虹殑璇︽儏
-     for (let i = 0; i < Math.min(applications.length, 10); i++) {
-      const app = applications[i];
-      console.log(`\n=== 鍙傝禌浜� ${i + 1} (ID: ${app.id}) ===`);
-      console.log(`  濮撳悕: ${app.playerName}`);
-      console.log(`  椤圭洰鍚�: ${app.projectName}`);
-      console.log(`  鐘舵��: ${app.state}`);
-
-      // 鏌ヨ璇︽儏
-       await checkPlayerDetail(app.id);
-     }
-
-  } catch (error) {
-    console.error('鏌ヨ澶辫触:', error.message);
-  }
-}
-
-checkSpecificPlayerData();
\ No newline at end of file
diff --git a/check-phone-db.js b/check-phone-db.js
deleted file mode 100644
index c4a5d4a..0000000
--- a/check-phone-db.js
+++ /dev/null
@@ -1,87 +0,0 @@
-const mysql = require('mysql2/promise');
-
-async function checkPhoneInDatabase() {
-    const connection = await mysql.createConnection({
-        host: '139.155.104.10',
-        port: 3306,
-        user: 'ryc',
-        password: 'KiYap3E8X8RLcM6T',
-        database: 'ryc',
-        connectTimeout: 60000
-    });
-
-    try {
-        console.log('=== 妫�鏌ユ暟鎹簱琛ㄧ粨鏋勫拰鎵嬫満鍙� 13981970816 ===\n');
-
-        // 1. 鏌ョ湅鎵�鏈夎〃
-        console.log('1. 鏌ョ湅鏁版嵁搴撲腑鐨勬墍鏈夎〃:');
-        const [tables] = await connection.execute('SHOW TABLES');
-        console.log('鏁版嵁搴撹〃:', tables.map(t => Object.values(t)[0]));
-        console.log('');
-
-        // 2. 鏌ョ湅鐢ㄦ埛鐩稿叧琛ㄧ殑缁撴瀯
-        const userTables = tables.filter(t => Object.values(t)[0].includes('user')).map(t => Object.values(t)[0]);
-        
-        for (const tableName of userTables) {
-            console.log(`${tableName} 琛ㄧ粨鏋�:`);
-            try {
-                const [structure] = await connection.execute(`DESCRIBE ${tableName}`);
-                structure.forEach(col => {
-                    console.log(`  ${col.Field}: ${col.Type}, Null: ${col.Null}, Key: ${col.Key}, Default: ${col.Default}`);
-                });
-                console.log('');
-            } catch (err) {
-                console.log(`  鏃犳硶鏌ョ湅 ${tableName} 琛ㄧ粨鏋�:`, err.message);
-            }
-        }
-
-        // 3. 鏌ユ壘鍖呭惈phone瀛楁鐨勮〃
-        console.log('3. 鏌ユ壘鍖呭惈phone瀛楁鐨勮〃:');
-        for (const table of tables.map(t => Object.values(t)[0])) {
-            try {
-                const [columns] = await connection.execute(`SHOW COLUMNS FROM ${table} LIKE '%phone%'`);
-                if (columns.length > 0) {
-                    console.log(`${table} 琛ㄥ寘鍚玴hone瀛楁:`, columns);
-                }
-            } catch (err) {
-                // 蹇界暐閿欒锛岀户缁笅涓�涓〃
-            }
-        }
-        console.log('');
-
-        // 4. 濡傛灉鎵惧埌浜嗙敤鎴疯〃锛屾煡璇㈡墜鏈哄彿
-        if (userTables.length > 0) {
-            const mainUserTable = userTables[0]; // 浣跨敤绗竴涓敤鎴疯〃
-            console.log(`4. 鍦� ${mainUserTable} 琛ㄤ腑鏌ユ壘鎵嬫満鍙� 13981970816:`);
-            
-            try {
-                // 鍏堟煡鐪嬭〃缁撴瀯纭畾瀛楁鍚�
-                const [structure] = await connection.execute(`DESCRIBE ${mainUserTable}`);
-                const phoneField = structure.find(col => col.Field.toLowerCase().includes('phone'));
-                const idField = structure.find(col => col.Field.toLowerCase().includes('id') && col.Key === 'PRI');
-                
-                if (phoneField) {
-                    const query = `SELECT * FROM ${mainUserTable} WHERE ${phoneField.Field} = ?`;
-                    const [users] = await connection.execute(query, ['13981970816']);
-                    console.log('鏌ヨ缁撴灉:', users);
-                    
-                    // 涔熸煡璇㈠寘鍚鎵嬫満鍙风殑璁板綍
-                    const likeQuery = `SELECT * FROM ${mainUserTable} WHERE ${phoneField.Field} LIKE ?`;
-                    const [likeUsers] = await connection.execute(likeQuery, ['%13981970816%']);
-                    console.log('妯$硦鏌ヨ缁撴灉:', likeUsers);
-                } else {
-                    console.log('鏈壘鍒皃hone瀛楁');
-                }
-            } catch (err) {
-                console.log('鏌ヨ鐢ㄦ埛琛ㄥ嚭閿�:', err.message);
-            }
-        }
-
-    } catch (error) {
-        console.error('鏁版嵁搴撴煡璇㈤敊璇�:', error);
-    } finally {
-        await connection.end();
-    }
-}
-
-checkPhoneInDatabase();
\ No newline at end of file
diff --git a/check-user-judge.js b/check-user-judge.js
deleted file mode 100644
index f15066c..0000000
--- a/check-user-judge.js
+++ /dev/null
@@ -1,75 +0,0 @@
-const mysql = require('mysql2/promise');
-
-async function checkUserJudgeInfo() {
-    const connection = await mysql.createConnection({
-        host: '139.155.104.10',
-        port: 3306,
-        user: 'ryc',
-        password: 'KiYap3E8X8RLcM6T',
-        database: 'ryc'
-    });
-
-    try {
-        console.log('妫�鏌ョ敤鎴稩D=152鐨勪俊鎭�...\n');
-
-        // 妫�鏌ョ敤鎴峰熀鏈俊鎭�
-        const [userRows] = await connection.execute(
-            'SELECT id, name, phone, wx_openid FROM t_user WHERE id = ?',
-            [152]
-        );
-        
-        if (userRows.length > 0) {
-            console.log('鐢ㄦ埛鍩烘湰淇℃伅:');
-            console.log(userRows[0]);
-        } else {
-            console.log('鏈壘鍒扮敤鎴稩D=152鐨勮褰�');
-            return;
-        }
-
-        // 妫�鏌ヨ瘎濮斾俊鎭�
-        const [judgeRows] = await connection.execute(
-            'SELECT id, user_id, name, title, company, state FROM t_judge WHERE user_id = ?',
-            [152]
-        );
-        
-        if (judgeRows.length > 0) {
-            console.log('\n璇勫淇℃伅:');
-            console.log(judgeRows[0]);
-        } else {
-            console.log('\n鏈壘鍒拌鐢ㄦ埛鐨勮瘎濮旇褰�');
-        }
-
-        // 妫�鏌ュ憳宸ヤ俊鎭�
-        const [employeeRows] = await connection.execute(
-            'SELECT id, user_id, name, phone, role_id, state FROM t_employee WHERE user_id = ?',
-            [152]
-        );
-        
-        if (employeeRows.length > 0) {
-            console.log('\n鍛樺伐淇℃伅:');
-            console.log(employeeRows[0]);
-        } else {
-            console.log('\n鏈壘鍒拌鐢ㄦ埛鐨勫憳宸ヨ褰�');
-        }
-
-        // 妫�鏌ュ鍛樹俊鎭�
-        const [playerRows] = await connection.execute(
-            'SELECT id, user_id, name, phone, state FROM t_player WHERE user_id = ?',
-            [152]
-        );
-        
-        if (playerRows.length > 0) {
-            console.log('\n瀛﹀憳淇℃伅:');
-            console.log(playerRows[0]);
-        } else {
-            console.log('\n鏈壘鍒拌鐢ㄦ埛鐨勫鍛樿褰�');
-        }
-
-    } catch (error) {
-        console.error('鏌ヨ澶辫触:', error);
-    } finally {
-        await connection.end();
-    }
-}
-
-checkUserJudgeInfo().catch(console.error);
\ No newline at end of file
diff --git a/clear-invalid-tokens.js b/clear-invalid-tokens.js
deleted file mode 100644
index f445506..0000000
--- a/clear-invalid-tokens.js
+++ /dev/null
@@ -1,86 +0,0 @@
-// 娓呯悊鏃犳晥token鐨勮剼鏈�
-// 杩欎釜鑴氭湰闇�瑕佸湪灏忕▼搴忓紑鍙戣�呭伐鍏风殑鎺у埗鍙颁腑杩愯
-
-console.log('馃Ч 寮�濮嬫竻鐞嗘棤鏁堢殑JWT token...');
-
-// 妫�鏌ュ綋鍓嶅瓨鍌ㄧ殑token
-const currentToken = wx.getStorageSync('token');
-console.log('褰撳墠token:', currentToken ? `${currentToken.substring(0, 20)}...` : '鏃�');
-
-// 妫�鏌oken鏍煎紡鏄惁鏈夋晥
-function isValidJWTFormat(token) {
-  if (!token || typeof token !== 'string') {
-    return false;
-  }
-  
-  // JWT搴旇鏈�3涓儴鍒嗭紝鐢�.鍒嗛殧
-  const parts = token.split('.');
-  if (parts.length !== 3) {
-    console.log('鉂� Token鏍煎紡鏃犳晥锛氫笉鏄�3涓儴鍒�');
-    return false;
-  }
-  
-  // 妫�鏌ユ槸鍚﹀寘鍚祴璇曠敤鐨勬棤鏁堢鍚�
-  if (token.includes('invalid_token')) {
-    console.log('鉂� 妫�娴嬪埌娴嬭瘯鐢ㄧ殑鏃犳晥token');
-    return false;
-  }
-  
-  try {
-    // 灏濊瘯瑙g爜header鍜宲ayload
-    const header = JSON.parse(atob(parts[0]));
-    const payload = JSON.parse(atob(parts[1]));
-    
-    console.log('Token header:', header);
-    console.log('Token payload:', payload);
-    
-    // 妫�鏌ユ槸鍚﹁繃鏈�
-    const now = Math.floor(Date.now() / 1000);
-    if (payload.exp && payload.exp < now) {
-      console.log('鉂� Token宸茶繃鏈�');
-      return false;
-    }
-    
-    console.log('鉁� Token鏍煎紡鏈夋晥');
-    return true;
-  } catch (e) {
-    console.log('鉂� Token瑙g爜澶辫触:', e.message);
-    return false;
-  }
-}
-
-// 娓呯悊鏃犳晥token
-if (currentToken) {
-  if (!isValidJWTFormat(currentToken)) {
-    console.log('馃棏锔� 娓呯悊鏃犳晥token...');
-    
-    // 娓呴櫎瀛樺偍鐨勮璇佷俊鎭�
-    wx.removeStorageSync('token');
-    wx.removeStorageSync('userInfo');
-    wx.removeStorageSync('sessionKey');
-    
-    // 娓呴櫎globalData涓殑璁よ瘉淇℃伅
-    const app = getApp();
-    if (app) {
-      app.globalData.token = null;
-      app.globalData.userInfo = null;
-      app.globalData.sessionKey = null;
-    }
-    
-    console.log('鉁� 鏃犳晥token宸叉竻鐞�');
-    console.log('馃挕 寤鸿閲嶆柊鍚姩灏忕▼搴忎互鑾峰彇鏂扮殑鏈夋晥token');
-  } else {
-    console.log('鉁� 褰撳墠token鏈夋晥锛屾棤闇�娓呯悊');
-  }
-} else {
-  console.log('鈩癸笍 褰撳墠娌℃湁瀛樺偍token');
-}
-
-console.log('馃帀 娓呯悊瀹屾垚锛�');
-
-// 浣跨敤璇存槑
-console.log('\n馃搵 浣跨敤璇存槑:');
-console.log('1. 鍦ㄥ皬绋嬪簭寮�鍙戣�呭伐鍏蜂腑鎵撳紑鎺у埗鍙�');
-console.log('2. 澶嶅埗骞剁矘璐磋繖娈典唬鐮�');
-console.log('3. 鎸夊洖杞︽墽琛�');
-console.log('4. 濡傛灉娓呯悊浜嗘棤鏁坱oken锛岃閲嶆柊鍚姩灏忕▼搴�');
\ No newline at end of file
diff --git a/debug-backend-user-context.js b/debug-backend-user-context.js
deleted file mode 100644
index 8ca4ded..0000000
--- a/debug-backend-user-context.js
+++ /dev/null
@@ -1,266 +0,0 @@
-const axios = require('axios');
-
-// 閰嶇疆
-const BASE_URL = 'http://localhost:8080/api';
-
-// 鑾峰彇鏂扮殑寰俊鐧诲綍token
-async function getNewWxToken() {
-    console.log('=== 鑾峰彇鏂扮殑寰俊鐧诲綍token ===');
-    
-    try {
-        // 浣跨敤GraphQL mutation杩涜寰俊鐧诲綍
-        const wxLoginMutation = `
-            mutation WxLogin($input: WxLoginRequest!) {
-                wxLogin(input: $input) {
-                    token
-                    userInfo {
-                        userId
-                        name
-                        phone
-                        userType
-                    }
-                    success
-                    message
-                    hasJudge
-                }
-            }
-        `;
-        
-        // 闇�瑕佷竴涓柊鐨勫井淇ode
-        const wxCode = 'NEED_NEW_WX_CODE_HERE'; // 璇锋浛鎹负鏂扮殑寰俊code
-        
-        if (wxCode === 'NEED_NEW_WX_CODE_HERE') {
-            console.log('鉂� 璇峰厛鏇挎崲鑴氭湰涓殑寰俊code');
-            return null;
-        }
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: wxLoginMutation,
-            variables: {
-                input: {
-                    code: wxCode,
-                    wxOpenid: "ogxxA1-KrSVTdqI9T1uaB1BQwPGU", // 浣跨敤宸茬煡鐨刼penid
-                    loginIp: "127.0.0.1",
-                    deviceInfo: "test-device",
-                    phoneAuthorized: false
-                }
-            }
-        });
-        
-        if (response.data.errors) {
-            console.log('鉂� 寰俊鐧诲綍澶辫触:', response.data.errors);
-            return null;
-        }
-        
-        const loginData = response.data.data.wxLogin;
-        console.log('鉁� 寰俊鐧诲綍鎴愬姛:');
-        console.log('- Token:', loginData.token ? '宸茬敓鎴�' : '鏈敓鎴�');
-        console.log('- 鐢ㄦ埛ID:', loginData.userInfo?.userId);
-        console.log('- 鐢ㄦ埛鍚�:', loginData.userInfo?.name);
-        console.log('- 鐢ㄦ埛绫诲瀷:', loginData.userInfo?.userType);
-        console.log('- 鏄惁鏈夎瘎濮旀潈闄�:', loginData.hasJudge);
-        
-        return loginData.token;
-        
-    } catch (error) {
-        console.error('鉂� 寰俊鐧诲綍澶辫触:', error.response?.data || error.message);
-        return null;
-    }
-}
-
-// 浣跨敤宸茬煡鐨則oken杩涜娴嬭瘯
-async function testWithKnownToken() {
-    console.log('\n=== 浣跨敤宸茬煡token娴嬭瘯 ===');
-    
-    // 杩欓噷浣跨敤涓�涓凡鐭ョ殑token锛堢敤鎴烽渶瑕佹彁渚涳級
-    const knownToken = 'PASTE_YOUR_VALID_TOKEN_HERE';
-    
-    if (knownToken === 'PASTE_YOUR_VALID_TOKEN_HERE') {
-        console.log('鉂� 璇峰厛鏇挎崲鑴氭湰涓殑token');
-        return;
-    }
-    
-    await debugUserContext(knownToken);
-}
-
-// 璋冭瘯鐢ㄦ埛涓婁笅鏂�
-async function debugUserContext(token) {
-    console.log('\n=== 璋冭瘯鐢ㄦ埛涓婁笅鏂� ===');
-    
-    // 1. 娴嬭瘯鑾峰彇褰撳墠鐢ㄦ埛淇℃伅
-    console.log('1. 娴嬭瘯鑾峰彇褰撳墠鐢ㄦ埛淇℃伅:');
-    try {
-        const userQuery = `
-            query GetCurrentUser {
-                currentUser {
-                    id
-                    name
-                    phone
-                }
-            }
-        `;
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: userQuery
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-        
-        if (response.data.errors) {
-            console.log('鉂� 鑾峰彇褰撳墠鐢ㄦ埛澶辫触:', response.data.errors);
-        } else if (response.data.data?.currentUser) {
-            const user = response.data.data.currentUser;
-            console.log('鉁� 褰撳墠鐢ㄦ埛淇℃伅:');
-            console.log(`   鐢ㄦ埛ID: ${user.id}`);
-            console.log(`   濮撳悕: ${user.name}`);
-            console.log(`   鎵嬫満鍙�: ${user.phone}`);
-        } else {
-            console.log('鈿狅笍  鏃犳硶鑾峰彇褰撳墠鐢ㄦ埛淇℃伅');
-        }
-    } catch (error) {
-        console.log('鉂� 鑾峰彇褰撳墠鐢ㄦ埛澶辫触:', error.response?.data || error.message);
-    }
-    
-    // 2. 娴嬭瘯鑾峰彇褰撳墠璇勫淇℃伅
-    console.log('\n2. 娴嬭瘯鑾峰彇褰撳墠璇勫淇℃伅:');
-    try {
-        const judgeQuery = `
-            query GetCurrentJudge {
-                currentJudgeInfo {
-                    judgeId
-                    judgeName
-                    title
-                    company
-                }
-            }
-        `;
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: judgeQuery
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-        
-        if (response.data.errors) {
-            console.log('鉂� 鑾峰彇褰撳墠璇勫澶辫触:', response.data.errors);
-        } else if (response.data.data?.currentJudgeInfo) {
-            const judge = response.data.data.currentJudgeInfo;
-            console.log('鉁� 褰撳墠璇勫淇℃伅:');
-            console.log(`   璇勫ID: ${judge.judgeId}`);
-            console.log(`   璇勫鍚�: ${judge.judgeName}`);
-            console.log(`   鑱屼綅: ${judge.title}`);
-            console.log(`   鍏徃: ${judge.company}`);
-        } else {
-            console.log('鈿狅笍  鏃犳硶鑾峰彇褰撳墠璇勫淇℃伅锛堝彲鑳戒笉鏄瘎濮旓級');
-        }
-    } catch (error) {
-        console.log('鉂� 鑾峰彇褰撳墠璇勫澶辫触:', error.response?.data || error.message);
-    }
-    
-    // 3. 娴嬭瘯璇勫缁熻鏌ヨ
-    console.log('\n3. 娴嬭瘯璇勫缁熻鏌ヨ:');
-    try {
-        const statsQuery = `
-            query GetReviewStats {
-                reviewStatistics {
-                    unReviewedCount
-                    reviewedCount
-                    studentUnReviewedCount
-                }
-            }
-        `;
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: statsQuery
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-        
-        if (response.data.errors) {
-            console.log('鉂� 鑾峰彇璇勫缁熻澶辫触:', response.data.errors);
-            response.data.errors.forEach(error => {
-                console.log(`   閿欒: ${error.message}`);
-                if (error.extensions) {
-                    console.log(`   鎵╁睍淇℃伅:`, error.extensions);
-                }
-            });
-        } else if (response.data.data?.reviewStatistics) {
-            const stats = response.data.data.reviewStatistics;
-            console.log('鉁� 璇勫缁熻:');
-            console.log(`   鏈瘎瀹�: ${stats.unReviewedCount}`);
-            console.log(`   宸茶瘎瀹�: ${stats.reviewedCount}`);
-            console.log(`   瀛﹀憳鏈瘎瀹�: ${stats.studentUnReviewedCount}`);
-        }
-    } catch (error) {
-        console.log('鉂� 鑾峰彇璇勫缁熻澶辫触:', error.response?.data || error.message);
-    }
-    
-    // 4. 娴嬭瘯绠�鍗曠殑璇勫椤圭洰鏌ヨ
-    console.log('\n4. 娴嬭瘯璇勫椤圭洰鏌ヨ:');
-    try {
-        const projectsQuery = `
-            query GetUnreviewedProjects {
-                unReviewedProjects(searchKeyword: "", page: 1, pageSize: 1) {
-                    totalCount
-                    currentPage
-                }
-            }
-        `;
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: projectsQuery
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-        
-        if (response.data.errors) {
-            console.log('鉂� 鑾峰彇璇勫椤圭洰澶辫触:', response.data.errors);
-            response.data.errors.forEach(error => {
-                console.log(`   閿欒: ${error.message}`);
-            });
-        } else if (response.data.data?.unReviewedProjects) {
-            const projects = response.data.data.unReviewedProjects;
-            console.log('鉁� 璇勫椤圭洰鏌ヨ鎴愬姛:');
-            console.log(`   鎬绘暟: ${projects.totalCount}`);
-            console.log(`   褰撳墠椤�: ${projects.currentPage}`);
-        }
-    } catch (error) {
-        console.log('鉂� 鑾峰彇璇勫椤圭洰澶辫触:', error.response?.data || error.message);
-    }
-}
-
-// 涓诲嚱鏁�
-async function main() {
-    console.log('馃攳 寮�濮嬭皟璇曞悗绔敤鎴蜂笂涓嬫枃闂...\n');
-    
-    // 閫夋嫨娴嬭瘯鏂瑰紡
-    const useNewToken = false; // 璁剧疆涓簍rue浣跨敤鏂皌oken锛宖alse浣跨敤宸茬煡token
-    
-    if (useNewToken) {
-        const token = await getNewWxToken();
-        if (token) {
-            await debugUserContext(token);
-        }
-    } else {
-        await testWithKnownToken();
-    }
-}
-
-// 杩愯涓诲嚱鏁�
-if (require.main === module) {
-    main();
-}
-
-module.exports = { debugUserContext, getNewWxToken };
\ No newline at end of file
diff --git a/debug-check-phone.js b/debug-check-phone.js
deleted file mode 100644
index 3c6dccc..0000000
--- a/debug-check-phone.js
+++ /dev/null
@@ -1,101 +0,0 @@
-const axios = require('axios');
-
-const API_BASE_URL = 'http://localhost:8080/api/graphql';
-const LOGIN_URL = 'http://localhost:8080/api/auth/web-login';
-
-// 鐧诲綍鍑芥暟
-async function login(phone, password) {
-    try {
-        const response = await axios.post(LOGIN_URL, {
-            phone: phone,
-            password: password
-        });
-
-        if (response.data.token) {
-            return response.data;
-        } else {
-            console.error('鐧诲綍澶辫触: 娌℃湁杩斿洖token');
-            return null;
-        }
-    } catch (error) {
-        console.error('鐧诲綍璇锋眰澶辫触:', error.message);
-        return null;
-    }
-}
-
-// 鏌ヨ鎵�鏈夊憳宸�
-async function getAllEmployees(token) {
-    const query = `
-        query {
-            employees {
-                id
-                name
-                phone
-                roleId
-                description
-                state
-                createTime
-                updateTime
-            }
-        }
-    `;
-
-    try {
-        const response = await axios.post(API_BASE_URL, {
-            query: query
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`
-            }
-        });
-
-        if (response.data.errors) {
-            console.error('鏌ヨ鍛樺伐澶辫触:', response.data.errors);
-            return [];
-        }
-
-        return response.data.data.employees;
-    } catch (error) {
-        console.error('鏌ヨ鍛樺伐璇锋眰澶辫触:', error.message);
-        return [];
-    }
-}
-
-async function main() {
-    console.log('寮�濮嬫鏌ユ墜鏈哄彿浣跨敤鎯呭喌...');
-    
-    // 浣跨敤绠$悊鍛樿处鍙风櫥褰�
-    const loginResult = await login('17898163888', '123456');
-    if (!loginResult) {
-        console.error('鐧诲綍澶辫触');
-        return;
-    }
-    
-    console.log('鐧诲綍鎴愬姛');
-    
-    // 鑾峰彇鎵�鏈夊憳宸�
-    const employees = await getAllEmployees(loginResult.token);
-    console.log(`\n鎵惧埌 ${employees.length} 涓憳宸�:`);
-    
-    employees.forEach(emp => {
-        console.log(`鍛樺伐ID: ${emp.id}, 濮撳悕: ${emp.name}, 鎵嬫満鍙�: ${emp.phone}, 瑙掕壊: ${emp.roleId}, 鐘舵��: ${emp.state}`);
-    });
-    
-    // 妫�鏌ョ壒瀹氭墜鏈哄彿
-    const targetPhones = ['13981970816', '13512331233'];
-    
-    targetPhones.forEach(targetPhone => {
-        const existingEmployee = employees.find(emp => emp.phone === targetPhone);
-        
-        if (existingEmployee) {
-            console.log(`\n鉂� 鎵嬫満鍙� ${targetPhone} 宸茶鍛樺伐浣跨敤:`);
-            console.log(`   鍛樺伐ID: ${existingEmployee.id}`);
-            console.log(`   鍛樺伐濮撳悕: ${existingEmployee.name}`);
-            console.log(`   瑙掕壊: ${existingEmployee.roleId}`);
-        } else {
-            console.log(`\n鉁� 鎵嬫満鍙� ${targetPhone} 鏈浠讳綍鍛樺伐浣跨敤`);
-        }
-    });
-}
-
-main().catch(console.error);
\ No newline at end of file
diff --git a/debug-dataset-issue.md b/debug-dataset-issue.md
deleted file mode 100644
index bfb4134..0000000
--- a/debug-dataset-issue.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# onDetailTap 涓� dataset.id 閿欒闂璋冭瘯鎶ュ憡
-
-## 闂鎻忚堪
-鐢ㄦ埛鍙嶉 `onDetailTap` 涓娇鐢ㄧ殑 `dataset.id` 鏄敊璇殑銆�
-
-## 宸叉鏌ョ殑鍐呭
-
-### 1. 妯℃澘缁戝畾妫�鏌�
-- 鉁� 妯℃澘涓纭娇鐢ㄤ簡 `data-id="{{item.id}}"`
-- 鉁� 鎸夐挳姝g‘缁戝畾浜� `catchtap="onDetailTap"`
-- 鉁� 淇浜嗘嫾鍐欓敊璇細`<viwe>` 鈫� `<view>`
-
-### 2. 鏁版嵁娴佹鏌�
-- 鉁� GraphQL 鏌ヨ杩斿洖鐨勬椿鍔ㄦ暟鎹腑 ID 瀛楁姝e父
-- 鉁� 鎵�鏈夋椿鍔� ID 閮芥槸瀛楃涓茬被鍨�
-- 鉁� `onDetailTap` 鍑芥暟姝g‘鑾峰彇 `e.currentTarget.dataset.id`
-- 鉁� `goToActivityDetail` 鍑芥暟姝g‘浼犻�掑弬鏁�
-
-### 3. 鍙傛暟浼犻�掓鏌�
-- 鉁� `utils.navigateTo` 姝g‘鏋勫缓 URL 鍙傛暟
-- 鉁� 璇︽儏椤� `onLoad` 姝g‘鎺ユ敹 `options.id` 鍙傛暟
-
-## 鍙兘鐨勯棶棰樺師鍥�
-
-### 1. 浜嬩欢鍐掓场闂
-铏界劧浣跨敤浜� `catchtap` 闃绘鍐掓场锛屼絾鍙兘瀛樺湪鍏朵粬鐐瑰嚮浜嬩欢骞叉壈銆�
-
-### 2. 鏁版嵁鏇存柊鏃舵満闂
-鍙兘鍦ㄦ暟鎹繕鏈畬鍏ㄥ姞杞芥椂灏辫Е鍙戜簡鐐瑰嚮浜嬩欢銆�
-
-### 3. 灏忕▼搴忕幆澧冪壒娈婃��
-鍦ㄥ疄闄呭皬绋嬪簭鐜涓彲鑳藉瓨鍦ㄤ笌娴嬭瘯鐜涓嶅悓鐨勮涓恒��
-
-### 4. 缂撳瓨闂
-鍙兘瀛樺湪鏃т唬鐮佺紦瀛樺鑷寸殑闂銆�
-
-## 寤鸿鐨勮皟璇曟楠�
-
-### 1. 娣诲姞璋冭瘯鏃ュ織
-鍦� `onDetailTap` 鍑芥暟涓坊鍔犺缁嗙殑璋冭瘯鏃ュ織锛�
-
-```javascript
-onDetailTap(e) {
-  console.log('=== onDetailTap 璋冭瘯淇℃伅 ===');
-  console.log('浜嬩欢瀵硅薄:', e);
-  console.log('currentTarget:', e.currentTarget);
-  console.log('dataset:', e.currentTarget.dataset);
-  console.log('dataset.id:', e.currentTarget.dataset.id);
-  console.log('dataset.id 绫诲瀷:', typeof e.currentTarget.dataset.id);
-  
-  const id = e.currentTarget.dataset.id;
-  if (id) {
-    console.log('鍑嗗璺宠浆锛孖D:', id);
-    this.goToActivityDetail(id);
-  } else {
-    console.log('鉂� ID 涓虹┖鎴栨湭瀹氫箟');
-    console.log('瀹屾暣 dataset:', JSON.stringify(e.currentTarget.dataset));
-  }
-}
-```
-
-### 2. 妫�鏌ユ暟鎹姞杞界姸鎬�
-纭繚鍦ㄦ暟鎹畬鍏ㄥ姞杞藉悗鎵嶆樉绀烘寜閽細
-
-```xml
-<button 
-  wx:if="{{item.id}}"
-  class="ghost-btn" 
-  catchtap="onDetailTap" 
-  data-id="{{item.id}}"
->
-  鏌ョ湅璇︽儏
-</button>
-```
-
-### 3. 娓呴櫎缂撳瓨
-- 娓呴櫎灏忕▼搴忕紦瀛�
-- 閲嶆柊缂栬瘧椤圭洰
-- 閲嶅惎寮�鍙戣�呭伐鍏�
-
-## 褰撳墠鐘舵��
-- 鉁� 淇浜嗘ā鏉夸腑鐨勬嫾鍐欓敊璇�
-- 鉁� 楠岃瘉浜嗘暟鎹祦鐨勬纭��
-- 鈴� 绛夊緟鐢ㄦ埛鍙嶉鍏蜂綋鐨勯敊璇儏鍐�
-
-## 涓嬩竴姝ヨ鍔�
-1. 璇风敤鎴锋彁渚涘叿浣撶殑閿欒淇℃伅鎴栨埅鍥�
-2. 鍦ㄥ疄闄呯幆澧冧腑娣诲姞璋冭瘯鏃ュ織
-3. 妫�鏌ユ槸鍚﹀瓨鍦ㄧ壒瀹氭潯浠朵笅鐨勯棶棰�
\ No newline at end of file
diff --git a/debug-employee-check.js b/debug-employee-check.js
deleted file mode 100644
index b1b271d..0000000
--- a/debug-employee-check.js
+++ /dev/null
@@ -1,133 +0,0 @@
-const axios = require('axios');
-
-const BASE_URL = 'http://localhost:8080';
-
-// 鐧诲綍鍑芥暟
-async function login() {
-    try {
-        const loginData = {
-            phone: '17898163888',
-            password: '123456'
-        };
-
-        console.log('姝e湪鐧诲綍...');
-        const response = await axios.post(`${BASE_URL}/api/auth/web-login`, loginData);
-        
-        if (response.status === 200 && response.data.success) {
-            console.log('鐧诲綍鎴愬姛');
-            return response.data.data.token;
-        } else {
-            console.error('鐧诲綍澶辫触:', response.data);
-            return null;
-        }
-    } catch (error) {
-        console.error('鐧诲綍璇锋眰澶辫触:', error.message);
-        return null;
-    }
-}
-
-// 鏌ヨ褰撳墠鐢ㄦ埛淇℃伅
-async function getCurrentUserInfo(token) {
-    try {
-        const query = `
-            query {
-                currentUser {
-                    id
-                    name
-                    phone
-                }
-            }
-        `;
-
-        const response = await axios.post(`${BASE_URL}/api/graphql`, {
-            query: query
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-
-        console.log('褰撳墠鐢ㄦ埛淇℃伅:', JSON.stringify(response.data, null, 2));
-        return response.data.data.currentUser;
-    } catch (error) {
-        console.error('鏌ヨ褰撳墠鐢ㄦ埛澶辫触:', error.message);
-        return null;
-    }
-}
-
-// 鏌ヨ鎵�鏈夊憳宸�
-async function getAllEmployees(token) {
-    try {
-        const query = `
-            query {
-                employees {
-                    id
-                    name
-                    phone
-                    roleId
-                    description
-                }
-            }
-        `;
-
-        const response = await axios.post(`${BASE_URL}/api/graphql`, {
-            query: query
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-
-        console.log('鎵�鏈夊憳宸ヤ俊鎭�:', JSON.stringify(response.data, null, 2));
-        return response.data.data.employees;
-    } catch (error) {
-        console.error('鏌ヨ鍛樺伐澶辫触:', error.message);
-        return null;
-    }
-}
-
-// 涓诲嚱鏁�
-async function main() {
-    console.log('=== 妫�鏌ュ綋鍓嶇敤鎴锋槸鍚﹀凡缁忔槸鍛樺伐 ===');
-    
-    // 1. 鐧诲綍
-    const token = await login();
-    if (!token) {
-        console.error('鐧诲綍澶辫触锛岄��鍑�');
-        return;
-    }
-
-    // 2. 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅
-    const currentUser = await getCurrentUserInfo(token);
-    if (!currentUser) {
-        console.error('鑾峰彇褰撳墠鐢ㄦ埛淇℃伅澶辫触');
-        return;
-    }
-
-    // 3. 鑾峰彇鎵�鏈夊憳宸ヤ俊鎭�
-    const employees = await getAllEmployees(token);
-    if (!employees) {
-        console.error('鑾峰彇鍛樺伐淇℃伅澶辫触');
-        return;
-    }
-
-    // 4. 妫�鏌ュ綋鍓嶇敤鎴锋槸鍚﹀凡缁忔槸鍛樺伐
-    const currentUserPhone = currentUser.phone;
-    const isEmployee = employees.some(emp => emp.phone === currentUserPhone);
-    
-    console.log('\n=== 妫�鏌ョ粨鏋� ===');
-    console.log(`褰撳墠鐢ㄦ埛鎵嬫満鍙�: ${currentUserPhone}`);
-    console.log(`鏄惁宸茬粡鏄憳宸�: ${isEmployee}`);
-    
-    if (isEmployee) {
-        const employeeInfo = employees.find(emp => emp.phone === currentUserPhone);
-        console.log('鍛樺伐淇℃伅:', employeeInfo);
-        console.log('\n杩欏氨鏄负浠�涔堝垱寤烘柊鍛樺伐澶辫触鐨勫師鍥狅細褰撳墠鐧诲綍鐢ㄦ埛宸茬粡鏄憳宸ヤ簡锛�');
-    } else {
-        console.log('褰撳墠鐢ㄦ埛涓嶆槸鍛樺伐锛屽彲浠ュ垱寤烘柊鍛樺伐璁板綍');
-    }
-}
-
-main().catch(console.error);
\ No newline at end of file
diff --git a/debug-employee.js b/debug-employee.js
deleted file mode 100644
index 396aac3..0000000
--- a/debug-employee.js
+++ /dev/null
@@ -1,63 +0,0 @@
-const axios = require('axios');
-
-async function debugEmployeeSave() {
-  try {
-    console.log('1. 鐧诲綍鑾峰彇token...');
-    const loginResponse = await axios.post('http://localhost:8080/api/auth/web-login', {
-      phone: '17898163888',
-      password: '123456'
-    });
-    
-    const token = loginResponse.data.token;
-    console.log('鐧诲綍鎴愬姛');
-    
-    // 2. 灏濊瘯淇濆瓨鍛樺伐
-    console.log('2. 淇濆瓨鍛樺伐...');
-    const uniquePhone = '139' + Date.now().toString().slice(-8);
-    console.log('浣跨敤鎵嬫満鍙�:', uniquePhone);
-    
-    const employeeData = {
-      name: '璋冭瘯鍛樺伐' + Date.now(),
-      phone: uniquePhone,
-      password: '123456',
-      roleId: 'EMPLOYEE',
-      description: '璋冭瘯鍛樺伐鎻忚堪'
-    };
-    
-    console.log('鍛樺伐鏁版嵁:', JSON.stringify(employeeData, null, 2));
-    
-    const saveResponse = await axios.post('http://localhost:8080/api/graphql', {
-      query: `
-        mutation SaveEmployee($input: EmployeeInput!) {
-          saveEmployee(input: $input) {
-            id
-            name
-            phone
-            roleId
-            description
-          }
-        }
-      `,
-      variables: {
-        input: employeeData
-      }
-    }, {
-      headers: {
-        'Authorization': `Bearer ${token}`,
-        'Content-Type': 'application/json'
-      }
-    });
-    
-    console.log('淇濆瓨鍝嶅簲鐘舵��:', saveResponse.status);
-    console.log('淇濆瓨鍝嶅簲鏁版嵁:', JSON.stringify(saveResponse.data, null, 2));
-    
-  } catch (error) {
-    console.error('閿欒:', error.message);
-    if (error.response) {
-      console.error('鍝嶅簲鐘舵��:', error.response.status);
-      console.error('鍝嶅簲鏁版嵁:', JSON.stringify(error.response.data, null, 2));
-    }
-  }
-}
-
-debugEmployeeSave();
\ No newline at end of file
diff --git a/debug-gender-education-data.js b/debug-gender-education-data.js
deleted file mode 100644
index a772b1d..0000000
--- a/debug-gender-education-data.js
+++ /dev/null
@@ -1,90 +0,0 @@
-// 璋冭瘯鎬у埆鍜屽鍘嗘樉绀洪棶棰�
-const axios = require('axios');
-
-async function testGenderEducationData() {
-  try {
-    // 浣跨敤GraphQL鏌ヨ鑾峰彇瀹為檯鐨勫弬璧涗汉璇︽儏鏁版嵁
-    const query = `
-      query GetActivityPlayerDetail($id: ID!) {
-        activityPlayerDetail(id: $id) {
-          id
-          projectName
-          description
-          playerInfo {
-            id
-            name
-            gender
-            birthday
-            education
-            userInfo {
-              avatarUrl
-            }
-          }
-          regionInfo {
-            id
-            name
-          }
-        }
-      }
-    `;
-
-    // 娴嬭瘯鍑犱釜涓嶅悓鐨勫弬璧涗汉ID
-    const testIds = [1, 2, 3]; // 鍙互鏍规嵁瀹為檯鏁版嵁璋冩暣
-
-    for (const id of testIds) {
-      console.log(`\n=== 娴嬭瘯鍙傝禌浜篒D: ${id} ===`);
-      
-      try {
-        const response = await axios.post('http://localhost:8080/api/graphql', {
-          query: query,
-          variables: { id: id.toString() }
-        }, {
-          headers: {
-            'Content-Type': 'application/json'
-          }
-        });
-
-        if (response.data.errors) {
-          console.log('GraphQL閿欒:', response.data.errors);
-          continue;
-        }
-
-        const detail = response.data.data?.activityPlayerDetail;
-        if (!detail) {
-          console.log('鏈壘鍒版暟鎹�');
-          continue;
-        }
-
-        console.log('鍘熷鍚庣鏁版嵁:');
-        console.log('  濮撳悕:', detail.playerInfo.name);
-        console.log('  鎬у埆 (鍘熷鍊�):', detail.playerInfo.gender, typeof detail.playerInfo.gender);
-        console.log('  瀛﹀巻 (鍘熷鍊�):', detail.playerInfo.education, typeof detail.playerInfo.education);
-        console.log('  鐢熸棩:', detail.playerInfo.birthday);
-        console.log('  鍦板尯:', detail.regionInfo?.name);
-
-        // 妯℃嫙鍓嶇鐨勬�у埆杞崲閫昏緫
-        function getGenderText(gender) {
-          console.log('  鎬у埆杞崲杈撳叆:', gender, typeof gender);
-          if (gender === 0) return '濂�'
-          if (gender === 1) return '鐢�'
-          return '鏈~鍐�'
-        }
-
-        const convertedGender = getGenderText(detail.playerInfo.gender);
-        console.log('  鎬у埆杞崲缁撴灉:', convertedGender);
-
-        // 妫�鏌ュ鍘嗗鐞�
-        const education = detail.playerInfo.education || '';
-        console.log('  瀛﹀巻澶勭悊缁撴灉:', education || '鏈~鍐�');
-
-      } catch (error) {
-        console.log(`ID ${id} 鏌ヨ澶辫触:`, error.message);
-      }
-    }
-
-  } catch (error) {
-    console.error('娴嬭瘯澶辫触:', error.message);
-  }
-}
-
-testGenderEducationData();
\ No newline at end of file
diff --git a/debug-jwt-token.js b/debug-jwt-token.js
deleted file mode 100644
index 3baf224..0000000
--- a/debug-jwt-token.js
+++ /dev/null
@@ -1,288 +0,0 @@
-const axios = require('axios');
-
-// 閰嶇疆
-const BASE_URL = 'http://localhost:8080/api';
-
-// JWT token瑙g爜鍑芥暟锛堜笉楠岃瘉绛惧悕锛屼粎鐢ㄤ簬璋冭瘯锛�
-function decodeJwtToken(token) {
-    try {
-        // JWT token鏍煎紡锛歨eader.payload.signature
-        const parts = token.split('.');
-        if (parts.length !== 3) {
-            throw new Error('Invalid JWT token format');
-        }
-        
-        // 瑙g爜header
-        const header = JSON.parse(Buffer.from(parts[0], 'base64url').toString());
-        
-        // 瑙g爜payload
-        const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString());
-        
-        return {
-            header,
-            payload,
-            signature: parts[2]
-        };
-    } catch (error) {
-        console.error('JWT token瑙g爜澶辫触:', error.message);
-        return null;
-    }
-}
-
-// 娴嬭瘯JWT token鍐呭
-async function debugJwtToken(token) {
-    console.log('=== JWT Token 璋冭瘯 ===');
-    
-    if (!token || token === 'PASTE_YOUR_VALID_TOKEN_HERE') {
-        console.log('鉂� 璇锋彁渚涙湁鏁堢殑JWT token');
-        return;
-    }
-    
-    console.log('Token闀垮害:', token.length);
-    console.log('Token鍓�50瀛楃:', token.substring(0, 50) + '...');
-    
-    // 瑙g爜JWT token
-    const decoded = decodeJwtToken(token);
-    if (decoded) {
-        console.log('\n馃搵 JWT Token 鍐呭:');
-        console.log('Header:', JSON.stringify(decoded.header, null, 2));
-        console.log('Payload:', JSON.stringify(decoded.payload, null, 2));
-        
-        // 妫�鏌ュ叧閿瓧娈�
-        if (decoded.payload.userId) {
-            console.log(`\n馃攳 鐢ㄦ埛ID: ${decoded.payload.userId}`);
-            console.log(`   绫诲瀷: ${typeof decoded.payload.userId}`);
-            console.log(`   鏄惁涓鸿礋鏁�: ${decoded.payload.userId < 0}`);
-        } else {
-            console.log('\n鈿狅笍  Token涓病鏈夋壘鍒皍serId瀛楁');
-        }
-        
-        if (decoded.payload.sub) {
-            console.log(`\n馃攳 Subject: ${decoded.payload.sub}`);
-        }
-        
-        if (decoded.payload.exp) {
-            const expDate = new Date(decoded.payload.exp * 1000);
-            const now = new Date();
-            console.log(`\n鈴� Token杩囨湡鏃堕棿: ${expDate.toLocaleString()}`);
-            console.log(`   褰撳墠鏃堕棿: ${now.toLocaleString()}`);
-            console.log(`   鏄惁宸茶繃鏈�: ${now > expDate}`);
-        }
-    }
-    
-    // 娴嬭瘯token楠岃瘉
-    console.log('\n=== Token 楠岃瘉娴嬭瘯 ===');
-    await testTokenValidation(token);
-}
-
-// 娴嬭瘯token楠岃瘉
-async function testTokenValidation(token) {
-    try {
-        // 1. 娴嬭瘯绠�鍗曠殑GraphQL鏌ヨ
-        console.log('1. 娴嬭瘯绠�鍗曟煡璇�:');
-        const simpleQuery = `
-            query {
-                __typename
-            }
-        `;
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: simpleQuery
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-        
-        if (response.status === 200) {
-            console.log('鉁� Token楠岃瘉閫氳繃锛堝熀纭�鏌ヨ鎴愬姛锛�');
-        } else {
-            console.log('鉂� Token楠岃瘉澶辫触锛岀姸鎬佺爜:', response.status);
-        }
-        
-    } catch (error) {
-        console.log('鉂� Token楠岃瘉澶辫触:', error.response?.status, error.response?.data || error.message);
-    }
-    
-    try {
-        // 2. 娴嬭瘯闇�瑕佽璇佺殑鏌ヨ
-        console.log('\n2. 娴嬭瘯璁よ瘉鏌ヨ:');
-        const authQuery = `
-            query GetCurrentUser {
-                currentUser {
-                    id
-                    name
-                    phone
-                }
-            }
-        `;
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: authQuery
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-        
-        if (response.data.errors) {
-            console.log('鉂� 璁よ瘉鏌ヨ澶辫触:', response.data.errors);
-        } else if (response.data.data?.currentUser) {
-            const user = response.data.data.currentUser;
-            console.log('鉁� 璁よ瘉鏌ヨ鎴愬姛:');
-            console.log(`   鐢ㄦ埛ID: ${user.id}`);
-            console.log(`   濮撳悕: ${user.name}`);
-            console.log(`   鎵嬫満鍙�: ${user.phone}`);
-            
-            // 妫�鏌ョ敤鎴稩D鏄惁鍖归厤
-            if (user.id === 152) {
-                console.log('鉁� 鐢ㄦ埛ID鍖归厤锛�152锛�');
-            } else {
-                console.log(`鈿狅笍  鐢ㄦ埛ID涓嶅尮閰嶏紝鏈熸湜152锛屽疄闄�${user.id}`);
-            }
-        } else {
-            console.log('鈿狅笍  璁よ瘉鏌ヨ杩斿洖绌虹粨鏋�');
-        }
-        
-    } catch (error) {
-        console.log('鉂� 璁よ瘉鏌ヨ澶辫触:', error.response?.status, error.response?.data || error.message);
-    }
-    
-    try {
-        // 3. 娴嬭瘯璇勫鏌ヨ
-        console.log('\n3. 娴嬭瘯璇勫鏌ヨ:');
-        const judgeQuery = `
-            query GetCurrentJudge {
-                currentJudgeInfo {
-                    judgeId
-                    judgeName
-                    title
-                    company
-                }
-            }
-        `;
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: judgeQuery
-        }, {
-            headers: {
-                'Authorization': `Bearer ${token}`,
-                'Content-Type': 'application/json'
-            }
-        });
-        
-        if (response.data.errors) {
-            console.log('鉂� 璇勫鏌ヨ澶辫触:', response.data.errors);
-        } else if (response.data.data?.currentJudgeInfo) {
-            const judge = response.data.data.currentJudgeInfo;
-            console.log('鉁� 璇勫鏌ヨ鎴愬姛:');
-            console.log(`   璇勫ID: ${judge.judgeId}`);
-            console.log(`   璇勫鍚�: ${judge.judgeName}`);
-            
-            // 妫�鏌ヨ瘎濮擨D鏄惁鍖归厤
-            if (judge.judgeId === 72) {
-                console.log('鉁� 璇勫ID鍖归厤锛�72锛�');
-            } else {
-                console.log(`鈿狅笍  璇勫ID涓嶅尮閰嶏紝鏈熸湜72锛屽疄闄�${judge.judgeId}`);
-            }
-        } else {
-            console.log('鈿狅笍  璇勫鏌ヨ杩斿洖绌虹粨鏋滐紙鍙兘涓嶆槸璇勫锛�');
-        }
-        
-    } catch (error) {
-        console.log('鉂� 璇勫鏌ヨ澶辫触:', error.response?.status, error.response?.data || error.message);
-    }
-}
-
-// 鑾峰彇鏂扮殑寰俊鐧诲綍token骞惰皟璇�
-async function getAndDebugNewToken() {
-    console.log('=== 鑾峰彇鏂扮殑寰俊鐧诲綍token ===');
-    
-    try {
-        const wxLoginMutation = `
-            mutation WxLogin($input: WxLoginRequest!) {
-                wxLogin(input: $input) {
-                    token
-                    userInfo {
-                        userId
-                        name
-                        phone
-                        userType
-                    }
-                    success
-                    message
-                    hasJudge
-                }
-            }
-        `;
-        
-        // 闇�瑕佷竴涓柊鐨勫井淇ode
-        const wxCode = 'NEED_NEW_WX_CODE_HERE'; // 璇锋浛鎹负鏂扮殑寰俊code
-        
-        if (wxCode === 'NEED_NEW_WX_CODE_HERE') {
-            console.log('鉂� 璇峰厛鏇挎崲鑴氭湰涓殑寰俊code');
-            return;
-        }
-        
-        const response = await axios.post(`${BASE_URL}/graphql`, {
-            query: wxLoginMutation,
-            variables: {
-                input: {
-                    code: wxCode,
-                    wxOpenid: "ogxxA1-KrSVTdqI9T1uaB1BQwPGU", // 浣跨敤宸茬煡鐨刼penid
-                    loginIp: "127.0.0.1",
-                    deviceInfo: "test-device",
-                    phoneAuthorized: false
-                }
-            }
-        });
-        
-        if (response.data.errors) {
-            console.log('鉂� 寰俊鐧诲綍澶辫触:', response.data.errors);
-            return;
-        }
-        
-        const loginData = response.data.data.wxLogin;
-        console.log('鉁� 寰俊鐧诲綍鎴愬姛:');
-        console.log('- 鐢ㄦ埛ID:', loginData.userInfo?.userId);
-        console.log('- 鐢ㄦ埛鍚�:', loginData.userInfo?.name);
-        console.log('- 鐢ㄦ埛绫诲瀷:', loginData.userInfo?.userType);
-        console.log('- 鏄惁鏈夎瘎濮旀潈闄�:', loginData.hasJudge);
-        
-        if (loginData.token) {
-            console.log('\n寮�濮嬭皟璇曟柊鑾峰彇鐨則oken...');
-            await debugJwtToken(loginData.token);
-        } else {
-            console.log('鉂� 鏈幏鍙栧埌token');
-        }
-        
-    } catch (error) {
-        console.error('鉂� 寰俊鐧诲綍澶辫触:', error.response?.data || error.message);
-    }
-}
-
-// 涓诲嚱鏁�
-async function main() {
-    console.log('馃攳 寮�濮婮WT Token璋冭瘯...\n');
-    
-    // 閫夋嫨璋冭瘯鏂瑰紡
-    const useExistingToken = true; // 璁剧疆涓簍rue浣跨敤宸叉湁token锛宖alse鑾峰彇鏂皌oken
-    
-    if (useExistingToken) {
-        // 浣跨敤宸叉湁鐨則oken杩涜璋冭瘯
-        const existingToken = 'PASTE_YOUR_VALID_TOKEN_HERE'; // 璇锋浛鎹负浣犵殑token
-        await debugJwtToken(existingToken);
-    } else {
-        // 鑾峰彇鏂皌oken骞惰皟璇�
-        await getAndDebugNewToken();
-    }
-}
-
-// 杩愯涓诲嚱鏁�
-if (require.main === module) {
-    main();
-}
-
-module.exports = { debugJwtToken, decodeJwtToken };
\ No newline at end of file
diff --git a/debug-saveUserInfo.js b/debug-saveUserInfo.js
deleted file mode 100644
index c2c0311..0000000
--- a/debug-saveUserInfo.js
+++ /dev/null
@@ -1,148 +0,0 @@
-// 璋冭瘯saveUserInfo GraphQL璇锋眰鐨勮剼鏈�
-const fetch = require('node-fetch');
-
-console.log('=== 璋冭瘯saveUserInfo GraphQL璇锋眰 ===');
-
-// 娴嬭瘯鐢ㄧ殑token锛堣鏇挎崲涓哄疄闄呯殑token锛�
-const testToken = 'YOUR_TOKEN_HERE'; // 璇蜂粠灏忕▼搴忔帶鍒跺彴鑾峰彇瀹為檯token
-
-// GraphQL鏌ヨ
-const mutation = `
-  mutation SaveUserInfo($input: UserInput!) {
-    saveUserInfo(input: $input) {
-      id
-      name
-      avatar
-      phone
-      gender
-      birthday
-      wxOpenId
-      unionId
-    }
-  }
-`;
-
-const variables = {
-  input: {
-    name: "娴嬭瘯鐢ㄦ埛",
-    phone: "13981970816",
-    gender: "MALE",
-    birthday: "2025-10-07"
-  }
-};
-
-async function testSaveUserInfo() {
-  console.log('\n--- 娴嬭瘯鍙傛暟 ---');
-  console.log('GraphQL Endpoint:', 'http://localhost:8080/api/graphql');
-  console.log('Token:', testToken ? `${testToken.substring(0, 20)}...` : '鏃�');
-  console.log('Variables:', JSON.stringify(variables, null, 2));
-  
-  if (!testToken || testToken === 'YOUR_TOKEN_HERE') {
-    console.log('\n鉂� 璇峰厛璁剧疆鏈夋晥鐨則oken');
-    console.log('馃挕 鍦ㄥ皬绋嬪簭寮�鍙戣�呭伐鍏锋帶鍒跺彴杩愯: console.log("token:", wx.getStorageSync("token"))');
-    return;
-  }
-  
-  try {
-    console.log('\n--- 鍙戦�佽姹� ---');
-    
-    const headers = {
-      'Content-Type': 'application/json',
-      'Authorization': `Bearer ${testToken}`
-    };
-    
-    console.log('璇锋眰澶�:', headers);
-    
-    const response = await fetch('http://localhost:8080/api/graphql', {
-      method: 'POST',
-      headers: headers,
-      body: JSON.stringify({
-        query: mutation,
-        variables: variables
-      })
-    });
-    
-    console.log('\n--- 鍝嶅簲淇℃伅 ---');
-    console.log('鐘舵�佺爜:', response.status);
-    console.log('鐘舵�佹枃鏈�:', response.statusText);
-    
-    const responseText = await response.text();
-    console.log('鍝嶅簲鍐呭:', responseText);
-    
-    if (responseText) {
-      try {
-        const result = JSON.parse(responseText);
-        
-        if (result.errors) {
-          console.log('\n鉂� GraphQL閿欒:');
-          result.errors.forEach((error, index) => {
-            console.log(`閿欒 ${index + 1}:`, error.message);
-            if (error.extensions) {
-              console.log('鎵╁睍淇℃伅:', error.extensions);
-            }
-          });
-        }
-        
-        if (result.data && result.data.saveUserInfo) {
-          console.log('\n鉁� 淇濆瓨鎴愬姛:');
-          console.log(JSON.stringify(result.data.saveUserInfo, null, 2));
-        }
-        
-      } catch (parseError) {
-        console.log('\n鉂� JSON瑙f瀽澶辫触:', parseError.message);
-      }
-    }
-    
-  } catch (error) {
-    console.log('\n鉂� 璇锋眰澶辫触:', error.message);
-  }
-}
-
-// 娴嬭瘯token鏈夋晥鎬�
-function testTokenFormat(token) {
-  console.log('\n--- Token鏍煎紡妫�鏌� ---');
-  
-  if (!token || token === 'YOUR_TOKEN_HERE') {
-    console.log('鉂� 娌℃湁鎻愪緵token');
-    return false;
-  }
-  
-  const parts = token.split('.');
-  if (parts.length !== 3) {
-    console.log('鉂� Token鏍煎紡鏃犳晥锛堜笉鏄疛WT鏍煎紡锛�');
-    return false;
-  }
-  
-  try {
-    const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
-    console.log('Token payload:', {
-      userId: payload.userId,
-      phone: payload.phone,
-      exp: payload.exp ? new Date(payload.exp * 1000).toLocaleString() : 'N/A',
-      iat: payload.iat ? new Date(payload.iat * 1000).toLocaleString() : 'N/A'
-    });
-    
-    const now = Math.floor(Date.now() / 1000);
-    const isExpired = payload.exp && payload.exp <= now;
-    console.log('Token鏄惁杩囨湡:', isExpired);
-    
-    return !isExpired;
-  } catch (error) {
-    console.log('鉂� Token瑙f瀽澶辫触:', error.message);
-    return false;
-  }
-}
-
-// 鎵ц娴嬭瘯
-console.log('寮�濮嬫祴璇�...');
-const isValidToken = testTokenFormat(testToken);
-
-if (isValidToken) {
-  testSaveUserInfo();
-} else {
-  console.log('\n馃敡 瑙e喅姝ラ:');
-  console.log('1. 鍦ㄥ皬绋嬪簭寮�鍙戣�呭伐鍏锋帶鍒跺彴鑾峰彇token:');
-  console.log('   console.log("token:", wx.getStorageSync("token"))');
-  console.log('2. 灏嗚幏鍙栧埌鐨則oken鏇挎崲鑴氭湰涓殑 YOUR_TOKEN_HERE');
-  console.log('3. 閲嶆柊杩愯姝よ剼鏈�');
-}
\ No newline at end of file
diff --git a/debug-user-info.js b/debug-user-info.js
deleted file mode 100644
index 3364a11..0000000
--- a/debug-user-info.js
+++ /dev/null
@@ -1,222 +0,0 @@
-// 璋冭瘯灏忕▼搴忕敤鎴蜂俊鎭樉绀洪棶棰� - 淇鍚庣増鏈�
-console.log('=== 璋冭瘯灏忕▼搴忕敤鎴蜂俊鎭樉绀洪棶棰� - 淇鍚庣増鏈� ===');
-
-// 妯℃嫙灏忕▼搴忕幆澧� - 浣跨敤姝g‘鐨勭敤鎴蜂俊鎭粨鏋�
-const mockApp = {
-  globalData: {
-    userInfo: {
-      userId: 'test123',
-      name: '寮犱笁',
-      phone: '13800138000',
-      email: 'test@example.com',
-      avatar: 'https://example.com/avatar.jpg', // 鐩存帴鍦╱serInfo涓�
-      gender: 'MALE', // 瀛楃涓叉牸寮忔垨鏁板瓧鏍煎紡
-      birthday: '1990-05-15' // 鐩存帴鍦╱serInfo涓�
-    }
-  }
-};
-
-// 娴嬭瘯鏁板瓧鏍煎紡鐨勬�у埆
-const mockAppWithNumericGender = {
-  globalData: {
-    userInfo: {
-      userId: 'test456',
-      name: '鏉庡洓',
-      phone: '13900139000',
-      email: 'test2@example.com',
-      avatarUrl: 'https://example.com/avatar2.jpg', // 鍏煎鏃у瓧娈靛悕
-      gender: 1, // 鏁板瓧鏍煎紡锛�0涓虹敺锛�1涓哄コ
-      birthDate: '1992-08-20' // 鍏煎鏃у瓧娈靛悕
-    }
-  }
-};
-
-// 妯℃嫙椤甸潰鏁版嵁
-const pageData = {
-  formData: {
-    name: '',
-    phone: '',
-    email: '',
-    avatarUrl: '',
-    gender: null,
-    education: '',
-    birthDate: ''
-  },
-  displayUserInfo: {},
-  genderOptions: ['鐢�', '濂�'],
-  genderIndex: -1,
-  educationOptions: ['楂樹腑鍙婁互涓�', '澶т笓', '鏈', '纭曞+', '鍗氬+'],
-  educationIndex: -1,
-  localAvatarPath: ''
-};
-
-// 妯℃嫙淇鍚庣殑 loadUserInfo 鏂规硶
-function loadUserInfo(app = mockApp) {
-  console.log('\n--- 鎵ц淇鍚庣殑 loadUserInfo ---');
-  const userInfo = app.globalData.userInfo;
-  console.log('鍏ㄥ眬鐢ㄦ埛淇℃伅:', JSON.stringify(userInfo, null, 2));
-  
-  if (userInfo) {
-    let displayUserInfo = {
-      name: userInfo.name || '',
-      phone: userInfo.phone || '',
-      avatarUrl: userInfo.avatar || userInfo.avatarUrl || '',
-      gender: null,
-      education: '',
-      birthDate: ''
-    };
-    
-    // 澶勭悊鎬у埆淇℃伅 - 鐩存帴浠巙serInfo鑾峰彇
-    if (userInfo.gender !== undefined && userInfo.gender !== null) {
-      // 濡傛灉鏄瓧绗︿覆鏍煎紡锛岃浆鎹负鏁板瓧
-      if (typeof userInfo.gender === 'string') {
-        displayUserInfo.gender = userInfo.gender === 'MALE' ? 0 : 1;
-      } else {
-        displayUserInfo.gender = parseInt(userInfo.gender);
-      }
-    }
-    
-    // 澶勭悊鐢熸棩淇℃伅 - 鐩存帴浠巙serInfo鑾峰彇
-    if (userInfo.birthday || userInfo.birthDate) {
-      displayUserInfo.birthDate = userInfo.birthday || userInfo.birthDate;
-    }
-    
-    pageData.displayUserInfo = displayUserInfo;
-    console.log('璁剧疆 displayUserInfo:', JSON.stringify(pageData.displayUserInfo, null, 2));
-  }
-}
-
-// 妯℃嫙淇鍚庣殑 prefillUserInfo 鏂规硶
-function prefillUserInfo(app = mockApp) {
-  console.log('\n--- 鎵ц淇鍚庣殑 prefillUserInfo ---');
-  const userInfo = app.globalData.userInfo;
-  
-  if (userInfo) {
-    console.log('鐢ㄦ埛淇℃伅:', JSON.stringify(userInfo, null, 2));
-    
-    // 鏇存柊 formData
-    pageData.formData.name = userInfo.name || '';
-    pageData.formData.phone = userInfo.phone || '';
-    pageData.formData.email = userInfo.email || '';
-    pageData.formData.avatarUrl = userInfo.avatar || userInfo.avatarUrl || '';
-    
-    // 澶勭悊鎬у埆淇℃伅 - 鐩存帴浠巙serInfo鑾峰彇
-    if (userInfo.gender !== undefined && userInfo.gender !== null) {
-      let genderIndex;
-      // 濡傛灉鏄瓧绗︿覆鏍煎紡锛岃浆鎹负鏁板瓧
-      if (typeof userInfo.gender === 'string') {
-        genderIndex = userInfo.gender === 'MALE' ? 0 : 1;
-      } else {
-        genderIndex = parseInt(userInfo.gender);
-      }
-      pageData.formData.gender = genderIndex;
-      pageData.genderIndex = genderIndex;
-      console.log('璁剧疆鎬у埆:', genderIndex === 0 ? '鐢�' : '濂�', '绱㈠紩:', genderIndex);
-    }
-    
-    // 澶勭悊鐢熸棩淇℃伅 - 鐩存帴浠巙serInfo鑾峰彇
-    if (userInfo.birthday || userInfo.birthDate) {
-      const birthDate = userInfo.birthday || userInfo.birthDate;
-      pageData.formData.birthDate = birthDate;
-      console.log('璁剧疆鐢熸棩:', birthDate);
-    }
-    
-    console.log('鏈�缁� formData:', JSON.stringify(pageData.formData, null, 2));
-  }
-}
-
-// 妯℃嫙妯℃澘鏄剧ず閫昏緫
-function simulateTemplateDisplay() {
-  console.log('\n--- 妯℃嫙妯℃澘鏄剧ず ---');
-  
-  // 澶村儚鏄剧ず閫昏緫
-  const avatarSrc = pageData.localAvatarPath || pageData.formData.avatarUrl || '../../images/default-avatar.svg';
-  console.log('澶村儚鏄剧ず璺緞:', avatarSrc);
-  
-  // 鎬у埆鏄剧ず閫昏緫
-  const genderDisplay = pageData.formData.gender !== null ? 
-    pageData.genderOptions[pageData.formData.gender] : '璇烽�夋嫨鎬у埆';
-  console.log('鎬у埆鏄剧ず鏂囨湰:', genderDisplay);
-  
-  // 鐢熸棩鏄剧ず閫昏緫
-  const birthDateDisplay = pageData.formData.birthDate || '璇烽�夋嫨鐢熸棩';
-  console.log('鐢熸棩鏄剧ず鏂囨湰:', birthDateDisplay);
-  
-  // 妫�鏌ラ棶棰�
-  console.log('\n--- 闂璇婃柇 ---');
-  
-  if (!pageData.formData.avatarUrl) {
-    console.log('鉂� 澶村儚URL涓虹┖');
-  } else {
-    console.log('鉁� 澶村儚URL姝e父:', pageData.formData.avatarUrl);
-  }
-  
-  if (pageData.formData.gender === null || pageData.formData.gender === undefined) {
-    console.log('鉂� 鎬у埆鏈缃�');
-  } else {
-    console.log('鉁� 鎬у埆姝e父:', pageData.formData.gender, '鏄剧ず涓�:', genderDisplay);
-  }
-  
-  if (!pageData.formData.birthDate) {
-    console.log('鉂� 鐢熸棩鏈缃�');
-  } else {
-    console.log('鉁� 鐢熸棩姝e父:', pageData.formData.birthDate);
-  }
-}
-
-// 鎵ц娴嬭瘯
-console.log('\n=== 寮�濮嬫祴璇� - 瀛楃涓叉牸寮忔�у埆 ===');
-
-// 1. 娴嬭瘯瀛楃涓叉牸寮忕殑鐢ㄦ埛淇℃伅
-loadUserInfo(mockApp);
-prefillUserInfo(mockApp);
-
-// 妯℃嫙妯℃澘鏄剧ず閫昏緫
-console.log('\n--- 妯℃嫙妯℃澘鏄剧ず閫昏緫 (瀛楃涓叉牸寮�) ---');
-let avatarSrc = pageData.localAvatarPath || pageData.formData.avatarUrl || '/images/default-avatar.png';
-console.log('澶村儚鏄剧ず:', avatarSrc);
-
-let genderDisplay = pageData.genderIndex !== -1 ? pageData.genderOptions[pageData.genderIndex] : '璇烽�夋嫨鎬у埆';
-console.log('鎬у埆鏄剧ず:', genderDisplay);
-
-let birthDateDisplay = pageData.formData.birthDate || '璇烽�夋嫨鐢熸棩';
-console.log('鐢熸棩鏄剧ず:', birthDateDisplay);
-
-console.log('\n=== 寮�濮嬫祴璇� - 鏁板瓧鏍煎紡鎬у埆 ===');
-
-// 2. 閲嶇疆椤甸潰鏁版嵁
-pageData.formData = {
-  name: '',
-  phone: '',
-  email: '',
-  avatarUrl: '',
-  gender: -1,
-  education: '',
-  birthDate: ''
-};
-pageData.genderIndex = -1;
-pageData.educationIndex = -1;
-
-// 娴嬭瘯鏁板瓧鏍煎紡鐨勭敤鎴蜂俊鎭�
-loadUserInfo(mockAppWithNumericGender);
-prefillUserInfo(mockAppWithNumericGender);
-
-// 妯℃嫙妯℃澘鏄剧ず閫昏緫
-console.log('\n--- 妯℃嫙妯℃澘鏄剧ず閫昏緫 (鏁板瓧鏍煎紡) ---');
-avatarSrc = pageData.localAvatarPath || pageData.formData.avatarUrl || '/images/default-avatar.png';
-console.log('澶村儚鏄剧ず:', avatarSrc);
-
-genderDisplay = pageData.genderIndex !== -1 ? pageData.genderOptions[pageData.genderIndex] : '璇烽�夋嫨鎬у埆';
-console.log('鎬у埆鏄剧ず:', genderDisplay);
-
-birthDateDisplay = pageData.formData.birthDate || '璇烽�夋嫨鐢熸棩';
-console.log('鐢熸棩鏄剧ず:', birthDateDisplay);
-
-// 4. 璇婃柇缁撴灉
-console.log('\n=== 璇婃柇缁撴灉 ===');
-console.log('澶村儚URL:', pageData.formData.avatarUrl ? '鉁� 姝e父' : '鉁� 缂哄け');
-console.log('鎬у埆淇℃伅:', pageData.genderIndex !== -1 ? '鉁� 姝e父' : '鉁� 缂哄け');
-console.log('鐢熸棩淇℃伅:', pageData.formData.birthDate ? '鉁� 姝e父' : '鉁� 缂哄け');
-console.log('\n淇瀹屾垚锛佺敤鎴蜂俊鎭粨鏋勫凡缁熶竴锛屾敮鎸佸绉嶆牸寮忕殑鎬у埆鍜岀敓鏃ュ瓧娈点��');
-
-console.log('\n=== 璋冭瘯瀹屾垚 ===');
\ No newline at end of file
diff --git "a/doc/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx" "b/doc/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
new file mode 100644
index 0000000..35f5483
--- /dev/null
+++ "b/doc/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
Binary files differ
diff --git "a/doc/~$\351\200\211\350\257\204\345\210\206\350\241\250\357\274\2112025\345\271\264\346\210\220\346\270\235\345\276\267\347\234\211\350\265\204\345\210\233\344\270\232\345\244\247\350\265\233\346\265\267\351\200\211.doc" "b/doc/~$\351\200\211\350\257\204\345\210\206\350\241\250\357\274\2112025\345\271\264\346\210\220\346\270\235\345\276\267\347\234\211\350\265\204\345\210\233\344\270\232\345\244\247\350\265\233\346\265\267\351\200\211.doc"
new file mode 100644
index 0000000..dde355f
--- /dev/null
+++ "b/doc/~$\351\200\211\350\257\204\345\210\206\350\241\250\357\274\2112025\345\271\264\346\210\220\346\270\235\345\276\267\347\234\211\350\265\204\345\210\233\344\270\232\345\244\247\350\265\233\346\265\267\351\200\211.doc"
Binary files differ
diff --git "a/doc/\357\274\210\346\265\267\351\200\211\350\257\204\345\210\206\350\241\250\357\274\2112025\345\271\264\346\210\220\346\270\235\345\276\267\347\234\211\350\265\204\345\210\233\344\270\232\345\244\247\350\265\233\346\265\267\351\200\211.doc" "b/doc/\357\274\210\346\265\267\351\200\211\350\257\204\345\210\206\350\241\250\357\274\2112025\345\271\264\346\210\220\346\270\235\345\276\267\347\234\211\350\265\204\345\210\233\344\270\232\345\244\247\350\265\233\346\265\267\351\200\211.doc"
new file mode 100644
index 0000000..fa63e6d
--- /dev/null
+++ "b/doc/\357\274\210\346\265\267\351\200\211\350\257\204\345\210\206\350\241\250\357\274\2112025\345\271\264\346\210\220\346\270\235\345\276\267\347\234\211\350\265\204\345\210\233\344\270\232\345\244\247\350\265\233\346\265\267\351\200\211.doc"
Binary files differ
diff --git a/get-valid-token.js b/get-valid-token.js
deleted file mode 100644
index 1170a36..0000000
--- a/get-valid-token.js
+++ /dev/null
@@ -1,139 +0,0 @@
-const http = require('http');
-
-// 妯℃嫙寰俊鐧诲綍鑾峰彇鏈夋晥token
-function wxLogin() {
-    const postData = JSON.stringify({
-        code: '0d3mWR000zzp6V1526100pOK7a2mWR0W', // 鏇挎崲涓哄疄闄呯殑寰俊code
-        encryptedData: '',
-        iv: ''
-    });
-
-    const options = {
-        hostname: 'localhost',
-        port: 8080,
-        path: '/api/auth/wx-login',
-        method: 'POST',
-        headers: {
-            'Content-Type': 'application/json',
-            'Content-Length': Buffer.byteLength(postData)
-        }
-    };
-
-    console.log('鍙戦�佸井淇$櫥褰曡姹�...');
-
-    const req = http.request(options, (res) => {
-        console.log('鍝嶅簲鐘舵�佺爜:', res.statusCode);
-
-        let data = '';
-        res.on('data', (chunk) => {
-            data += chunk;
-        });
-
-        res.on('end', () => {
-            console.log('鍝嶅簲鍐呭:', data);
-            try {
-                const response = JSON.parse(data);
-                if (response.token) {
-                    console.log('\n鑾峰彇鍒皌oken:', response.token);
-                    console.log('鐢ㄦ埛淇℃伅:', response.userInfo);
-                    
-                    // 浣跨敤鑾峰彇鍒扮殑token娴嬭瘯saveUserInfo
-                    setTimeout(() => {
-                        testSaveUserInfo(response.token);
-                    }, 1000);
-                } else {
-                    console.log('鐧诲綍澶辫触:', response);
-                }
-            } catch (e) {
-                console.log('瑙f瀽鍝嶅簲澶辫触:', e.message);
-            }
-        });
-    });
-
-    req.on('error', (e) => {
-        console.error('璇锋眰閿欒:', e.message);
-    });
-
-    req.write(postData);
-    req.end();
-}
-
-// 浣跨敤鏈夋晥token娴嬭瘯saveUserInfo
-function testSaveUserInfo(token) {
-    console.log('\n=== 浣跨敤鏈夋晥token娴嬭瘯saveUserInfo ===');
-    
-    const mutation = `
-        mutation SaveUserInfo($input: UserInput!) {
-            saveUserInfo(input: $input) {
-                id
-                name
-                phone
-                gender
-                birthday
-                wxOpenId
-                unionId
-            }
-        }
-    `;
-
-    const variables = {
-        input: {
-            name: "娴嬭瘯鐢ㄦ埛JWT",
-            phone: "13981970816",
-            gender: "MALE",
-            birthday: "2025-10-07"
-        }
-    };
-
-    const postData = JSON.stringify({
-        query: mutation,
-        variables: variables
-    });
-
-    const options = {
-        hostname: 'localhost',
-        port: 8080,
-        path: '/api/graphql',
-        method: 'POST',
-        headers: {
-            'Content-Type': 'application/json',
-            'Content-Length': Buffer.byteLength(postData),
-            'Authorization': `Bearer ${token}`
-        }
-    };
-
-    console.log('鍙戦�丟raphQL璇锋眰锛屾惡甯︽湁鏁坱oken...');
-
-    const req = http.request(options, (res) => {
-        console.log('鍝嶅簲鐘舵�佺爜:', res.statusCode);
-
-        let data = '';
-        res.on('data', (chunk) => {
-            data += chunk;
-        });
-
-        res.on('end', () => {
-            console.log('鍝嶅簲鍐呭:', data);
-            try {
-                const response = JSON.parse(data);
-                if (response.errors) {
-                    console.log('GraphQL閿欒:', response.errors);
-                } else {
-                    console.log('淇濆瓨鐢ㄦ埛淇℃伅鎴愬姛:', response.data);
-                }
-            } catch (e) {
-                console.log('瑙f瀽鍝嶅簲澶辫触:', e.message);
-            }
-        });
-    });
-
-    req.on('error', (e) => {
-        console.error('璇锋眰閿欒:', e.message);
-    });
-
-    req.write(postData);
-    req.end();
-}
-
-// 寮�濮嬫祴璇�
-wxLogin();
\ No newline at end of file
diff --git a/simulate-miniprogram-debug.js b/simulate-miniprogram-debug.js
deleted file mode 100644
index 488aaab..0000000
--- a/simulate-miniprogram-debug.js
+++ /dev/null
@@ -1,145 +0,0 @@
-// 妯℃嫙灏忕▼搴忚皟璇� - 妫�鏌ョ敤鎴锋暟鎹粨鏋�
-console.log('=== 妯℃嫙灏忕▼搴忚皟璇曪細妫�鏌ョ敤鎴锋暟鎹粨鏋� ===\n');
-
-// 妯℃嫙浠庢湰鍦板瓨鍌ㄨ幏鍙栫殑鐢ㄦ埛鏁版嵁锛堝熀浜庡悗绔棩蹇楀垎鏋愶級
-const mockUserInfoFromStorage = {
-  userId: "-83348",
-  name: "娴嬭瘯鐢ㄦ埛",
-  phone: "138****8888",
-  avatar: "https://example.com/avatar.jpg",
-  roles: ["PLAYER"]
-  // 娉ㄦ剰锛氳繖閲屽彲鑳界己灏� gender 鍜� birthday 瀛楁
-};
-
-console.log('馃摫 妯℃嫙灏忕▼搴忓惎鍔�...');
-console.log('馃攳 浠庢湰鍦板瓨鍌ㄦ仮澶嶇殑鐢ㄦ埛鏁版嵁:', JSON.stringify(mockUserInfoFromStorage, null, 2));
-
-// 妯℃嫙娉ㄥ唽椤甸潰鐨� loadUserInfo 鏂规硶
-function simulateLoadUserInfo(userInfo) {
-  console.log('\n馃攧 妯℃嫙 loadUserInfo 鏂规硶鎵ц...');
-  console.log('馃攳 瀹為檯鐢ㄦ埛鏁版嵁:', JSON.stringify(userInfo, null, 2));
-  
-  if (userInfo) {
-    let displayUserInfo = {
-      name: userInfo.name || '',
-      phone: userInfo.phone || '',
-      avatarUrl: userInfo.avatar || userInfo.avatarUrl || '',
-      gender: null,
-      education: '',
-      birthDate: ''
-    };
-    
-    // 澶勭悊鎬у埆淇℃伅 - 鐩存帴浠巙serInfo鑾峰彇
-    if (userInfo.gender !== undefined && userInfo.gender !== null) {
-      console.log('馃懁 鍙戠幇鎬у埆瀛楁:', userInfo.gender, '绫诲瀷:', typeof userInfo.gender);
-      // 濡傛灉鏄瓧绗︿覆鏍煎紡锛岃浆鎹负鏁板瓧
-      if (typeof userInfo.gender === 'string') {
-        displayUserInfo.gender = userInfo.gender === 'MALE' ? 0 : 1;
-      } else {
-        displayUserInfo.gender = parseInt(userInfo.gender);
-      }
-      console.log('馃懁 澶勭悊鍚庣殑鎬у埆:', displayUserInfo.gender);
-    } else {
-      console.log('鉂� 鏈壘鍒版�у埆瀛楁鎴栧瓧娈典负绌�');
-    }
-    
-    // 澶勭悊鐢熸棩淇℃伅 - 鐩存帴浠巙serInfo鑾峰彇
-    if (userInfo.birthday || userInfo.birthDate) {
-      const birthDate = userInfo.birthday || userInfo.birthDate;
-      console.log('馃巶 鍙戠幇鐢熸棩瀛楁:', birthDate);
-      displayUserInfo.birthDate = birthDate;
-    } else {
-      console.log('鉂� 鏈壘鍒扮敓鏃ュ瓧娈� (birthday 鎴� birthDate)');
-    }
-    
-    console.log('鉁� 鏈�缁� displayUserInfo:', JSON.stringify(displayUserInfo, null, 2));
-    return displayUserInfo;
-  } else {
-    console.log('鈿狅笍 鏈壘鍒扮敤鎴蜂俊鎭�');
-    return null;
-  }
-}
-
-// 妯℃嫙娉ㄥ唽椤甸潰鐨� prefillUserInfo 鏂规硶
-function simulatePrefillUserInfo(userInfo) {
-  console.log('\n馃攧 妯℃嫙 prefillUserInfo 鏂规硶鎵ц...');
-  console.log('馃攳 棰勫~鍏呯敤鎴蜂俊鎭�:', userInfo);
-  
-  if (userInfo) {
-    const updateData = {
-      'formData.name': userInfo.name || '',
-      'formData.phone': userInfo.phone || '',
-      'formData.email': userInfo.email || '',
-      'formData.avatarUrl': userInfo.avatar || userInfo.avatarUrl || ''
-    };
-    
-    // 澶勭悊鎬у埆淇℃伅 - 鐩存帴浠巙serInfo鑾峰彇
-    if (userInfo.gender !== undefined && userInfo.gender !== null) {
-      let genderIndex;
-      console.log('馃懁 澶勭悊鎬у埆瀛楁:', userInfo.gender, '绫诲瀷:', typeof userInfo.gender);
-      // 濡傛灉鏄瓧绗︿覆鏍煎紡锛岃浆鎹负鏁板瓧
-      if (typeof userInfo.gender === 'string') {
-        genderIndex = userInfo.gender === 'MALE' ? 0 : 1;
-      } else {
-        genderIndex = parseInt(userInfo.gender);
-      }
-      updateData['formData.gender'] = genderIndex;
-      updateData['genderIndex'] = genderIndex;
-      console.log('馃懁 璁剧疆鎬у埆:', genderIndex === 0 ? '鐢�' : '濂�');
-    } else {
-      console.log('鉂� 鎬у埆瀛楁缂哄け锛屾棤娉曢濉厖');
-    }
-    
-    // 澶勭悊鐢熸棩淇℃伅 - 鐩存帴浠巙serInfo鑾峰彇
-    if (userInfo.birthday || userInfo.birthDate) {
-      const birthDate = userInfo.birthday || userInfo.birthDate;
-      updateData['formData.birthDate'] = birthDate;
-      console.log('馃巶 璁剧疆鐢熸棩:', birthDate);
-    } else {
-      console.log('鉂� 鐢熸棩瀛楁缂哄け锛屾棤娉曢濉厖');
-    }
-    
-    console.log('鉁� 棰勫~鍏呮暟鎹�:', JSON.stringify(updateData, null, 2));
-    console.log('馃柤锔� 璁剧疆澶村儚URL:', updateData['formData.avatarUrl']);
-    
-    return updateData;
-  } else {
-    console.log('鈿狅笍 鏈壘鍒扮敤鎴蜂俊鎭紝璺宠繃棰勫~鍏�');
-    return null;
-  }
-}
-
-// 鎵ц妯℃嫙娴嬭瘯
-console.log('\n=== 寮�濮嬫ā鎷熸祴璇� ===');
-
-// 娴嬭瘯1锛氱己灏戞�у埆鍜岀敓鏃ュ瓧娈电殑鎯呭喌
-console.log('\n馃搵 娴嬭瘯1锛氱己灏戞�у埆鍜岀敓鏃ュ瓧娈电殑鐢ㄦ埛鏁版嵁');
-simulateLoadUserInfo(mockUserInfoFromStorage);
-simulatePrefillUserInfo(mockUserInfoFromStorage);
-
-// 娴嬭瘯2锛氬寘鍚�у埆鍜岀敓鏃ュ瓧娈电殑鎯呭喌
-console.log('\n馃搵 娴嬭瘯2锛氬寘鍚�у埆鍜岀敓鏃ュ瓧娈电殑鐢ㄦ埛鏁版嵁');
-const completeUserInfo = {
-  ...mockUserInfoFromStorage,
-  gender: 0, // 鐢锋��
-  birthday: '1990-01-01'
-};
-simulateLoadUserInfo(completeUserInfo);
-simulatePrefillUserInfo(completeUserInfo);
-
-// 娴嬭瘯3锛氬瓧绗︿覆鏍煎紡鐨勬�у埆
-console.log('\n馃搵 娴嬭瘯3锛氬瓧绗︿覆鏍煎紡鐨勬�у埆');
-const stringGenderUserInfo = {
-  ...mockUserInfoFromStorage,
-  gender: 'MALE',
-  birthDate: '1995-05-15'
-};
-simulateLoadUserInfo(stringGenderUserInfo);
-simulatePrefillUserInfo(stringGenderUserInfo);
-
-console.log('\n=== 妯℃嫙娴嬭瘯瀹屾垚 ===');
-console.log('\n馃攳 鍒嗘瀽缁撴灉锛�');
-console.log('1. 濡傛灉鍚庣杩斿洖鐨勭敤鎴锋暟鎹腑缂哄皯 gender 鍜� birthday/birthDate 瀛楁锛�');
-console.log('   閭d箞杩欎簺瀛楁灏变笉浼氳棰勫~鍏呭埌琛ㄥ崟涓��');
-console.log('2. 闇�瑕佹鏌ュ悗绔� GraphQL 鏌ヨ鏄惁鍖呭惈浜嗚繖浜涘瓧娈点��');
-console.log('3. 闇�瑕佹鏌ュ悗绔暟鎹簱涓槸鍚﹀瓨鍌ㄤ簡杩欎簺瀛楁鐨勫�笺��');
\ No newline at end of file
diff --git a/test-image.svg b/test-image.svg
deleted file mode 100644
index 4746b23..0000000
--- a/test-image.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
-  <rect width="100" height="100" fill="#4CAF50"/>
-  <text x="50" y="55" text-anchor="middle" fill="white" font-family="Arial" font-size="14">娴嬭瘯鍥剧墖</text>
-</svg>
\ No newline at end of file
diff --git a/test_miniprogram_activities_fix.js b/test_miniprogram_activities_fix.js
deleted file mode 100644
index c9772ac..0000000
--- a/test_miniprogram_activities_fix.js
+++ /dev/null
@@ -1,142 +0,0 @@
-const mysql = require('mysql2/promise');
-
-// 鏁版嵁搴撻厤缃�
-const dbConfig = {
-  host: '139.155.104.10',
-  port: 3306,
-  user: 'ryc',
-  password: 'KiYap3E8X8RLcM6T',
-  database: 'ryc'
-};
-
-async function testActivityStatesAndMapping() {
-  let connection;
-  
-  try {
-    console.log('=== 灏忕▼搴忔椿鍔ㄧ姸鎬佹槧灏勪慨澶嶆祴璇� ===\n');
-    
-    // 杩炴帴鏁版嵁搴�
-    connection = await mysql.createConnection(dbConfig);
-    console.log('鉁� 鏁版嵁搴撹繛鎺ユ垚鍔�');
-    
-    // 1. 妫�鏌ユ暟鎹簱涓殑娲诲姩鐘舵�佸垎甯�
-    console.log('\n馃搳 鏁版嵁搴撴椿鍔ㄧ姸鎬佸垎甯�:');
-    const [stateStats] = await connection.execute(`
-      SELECT 
-        state,
-        COUNT(*) as count,
-        CASE 
-          WHEN state = 0 THEN '鏈彂甯�(鍒犻櫎)'
-          WHEN state = 1 THEN '鍙戝竷'
-          WHEN state = 2 THEN '鍏抽棴'
-          ELSE '鏈煡鐘舵��'
-        END as state_name
-      FROM t_activity 
-      WHERE pid = 0 
-      GROUP BY state 
-      ORDER BY state
-    `);
-    
-    stateStats.forEach(row => {
-      console.log(`  State ${row.state} (${row.state_name}): ${row.count} 涓椿鍔╜);
-    });
-    
-    // 2. 楠岃瘉淇鍚庣殑鐘舵�佹槧灏勯�昏緫
-    console.log('\n馃敡 淇鍚庣殑鐘舵�佹槧灏勯�昏緫:');
-    console.log('  upcoming (鍗冲皢寮�濮�) -> state=1 (鍙戝竷)');
-    console.log('  ongoing (杩涜涓�) -> state=1 (鍙戝竷)');
-    console.log('  ended (宸茬粨鏉�) -> state=2 (鍏抽棴)');
-    console.log('  all (鍏ㄩ儴) -> state=null (鍚庣杩囨护 state!=0)');
-    
-    // 3. 妯℃嫙涓嶅悓绛涢�夋潯浠剁殑鏌ヨ缁撴灉
-    console.log('\n馃И 妯℃嫙灏忕▼搴忕瓫閫夋煡璇�:');
-    
-    // 妯℃嫙 filterStatus = 'all' (state = null)
-    const [allActivities] = await connection.execute(`
-      SELECT id, name, state, 
-        CASE 
-          WHEN state = 0 THEN '鏈彂甯�(鍒犻櫎)'
-          WHEN state = 1 THEN '鍙戝竷'
-          WHEN state = 2 THEN '鍏抽棴'
-          ELSE '鏈煡鐘舵��'
-        END as state_name
-      FROM t_activity 
-      WHERE pid = 0 AND state != 0 
-      ORDER BY create_time DESC 
-      LIMIT 5
-    `);
-    console.log(`\n  绛涢�夋潯浠�: 鍏ㄩ儴 (all) - 搴旇鎺掗櫎宸插垹闄ゆ椿鍔�:`);
-    console.log(`  鏌ヨ缁撴灉: ${allActivities.length} 涓椿鍔╜);
-    allActivities.forEach(activity => {
-      console.log(`    - ${activity.name} (state=${activity.state}, ${activity.state_name})`);
-    });
-    
-    // 妯℃嫙 filterStatus = 'upcoming' 鎴� 'ongoing' (state = 1)
-    const [publishedActivities] = await connection.execute(`
-      SELECT id, name, state,
-        CASE 
-          WHEN state = 0 THEN '鏈彂甯�(鍒犻櫎)'
-          WHEN state = 1 THEN '鍙戝竷'
-          WHEN state = 2 THEN '鍏抽棴'
-          ELSE '鏈煡鐘舵��'
-        END as state_name
-      FROM t_activity 
-      WHERE pid = 0 AND state = 1 
-      ORDER BY create_time DESC 
-      LIMIT 5
-    `);
-    console.log(`\n  绛涢�夋潯浠�: 鍗冲皢寮�濮�/杩涜涓� (upcoming/ongoing) - state=1:`);
-    console.log(`  鏌ヨ缁撴灉: ${publishedActivities.length} 涓椿鍔╜);
-    publishedActivities.forEach(activity => {
-      console.log(`    - ${activity.name} (state=${activity.state}, ${activity.state_name})`);
-    });
-    
-    // 妯℃嫙 filterStatus = 'ended' (state = 2)
-    const [closedActivities] = await connection.execute(`
-      SELECT id, name, state,
-        CASE 
-          WHEN state = 0 THEN '鏈彂甯�(鍒犻櫎)'
-          WHEN state = 1 THEN '鍙戝竷'
-          WHEN state = 2 THEN '鍏抽棴'
-          ELSE '鏈煡鐘舵��'
-        END as state_name
-      FROM t_activity 
-      WHERE pid = 0 AND state = 2 
-      ORDER BY create_time DESC 
-      LIMIT 5
-    `);
-    console.log(`\n  绛涢�夋潯浠�: 宸茬粨鏉� (ended) - state=2:`);
-    console.log(`  鏌ヨ缁撴灉: ${closedActivities.length} 涓椿鍔╜);
-    closedActivities.forEach(activity => {
-      console.log(`    - ${activity.name} (state=${activity.state}, ${activity.state_name})`);
-    });
-    
-    // 4. 楠岃瘉鏄惁杩樻湁state=0鐨勬椿鍔ㄤ細琚樉绀�
-    const [deletedActivities] = await connection.execute(`
-      SELECT id, name, state 
-      FROM t_activity 
-      WHERE pid = 0 AND state = 0
-    `);
-    
-    console.log(`\n鉁� 楠岃瘉缁撴灉:`);
-    console.log(`  - 鏁版嵁搴撲腑鏈� ${deletedActivities.length} 涓凡鍒犻櫎娲诲姩 (state=0)`);
-    console.log(`  - 淇鍚庣殑鏄犲皠纭繚杩欎簺娲诲姩涓嶄細鍦ㄤ换浣曠瓫閫夋潯浠朵笅鏄剧ず`);
-    console.log(`  - 'all' 绛涢�変細鎺掗櫎 state=0 鐨勬椿鍔╜);
-    console.log(`  - 鍏朵粬绛涢�夋潯浠跺彧浼氭煡璇� state=1 鎴� state=2 鐨勬椿鍔╜);
-    
-    if (deletedActivities.length === 0) {
-      console.log(`  - 褰撳墠鏁版嵁搴撲腑娌℃湁宸插垹闄ょ殑娲诲姩锛屼慨澶嶄富瑕佽В鍐充簡鐘舵�佹槧灏勯棶棰榒);
-    }
-    
-  } catch (error) {
-    console.error('鉂� 娴嬭瘯澶辫触:', error.message);
-  } finally {
-    if (connection) {
-      await connection.end();
-      console.log('\n馃攲 鏁版嵁搴撹繛鎺ュ凡鍏抽棴');
-    }
-  }
-}
-
-// 杩愯娴嬭瘯
-testActivityStatesAndMapping();
\ No newline at end of file
diff --git a/test_miniprogram_fix.js b/test_miniprogram_fix.js
deleted file mode 100644
index 7f1d1d4..0000000
--- a/test_miniprogram_fix.js
+++ /dev/null
@@ -1,102 +0,0 @@
-// 娴嬭瘯灏忕▼搴忕淇鍚庣殑GraphQL鏌ヨ
-const axios = require('axios');
-
-const testQuery = `
-  query GetActivityPlayerDetail($id: ID!) {
-    activityPlayerDetail(id: $id) {
-      id
-      projectName
-      description
-      activityName
-      stageId
-      state
-      playerInfo {
-        id
-        name
-        phone
-        gender
-        birthday
-        education
-        introduction
-        userInfo {
-          userId
-          name
-          phone
-          avatarUrl
-        }
-      }
-      regionInfo {
-        id
-        name
-        fullPath
-      }
-      submissionFiles {
-        id
-        name
-        url
-        fullUrl
-        fileExt
-        fileSize
-        mediaType
-        thumbUrl
-        fullThumbUrl
-      }
-      ratingForm {
-        schemeId
-        schemeName
-        totalMaxScore
-        items {
-          id
-          name
-          maxScore
-          orderNo
-        }
-      }
-    }
-  }
-`;
-
-async function testGraphQLQuery() {
-  try {
-    console.log('娴嬭瘯淇鍚庣殑灏忕▼搴忕GraphQL鏌ヨ...');
-    
-    const response = await axios.post('http://localhost:8080/api/graphql', {
-      query: testQuery,
-      variables: {
-        id: "1" // 浣跨敤涓�涓祴璇旾D
-      }
-    }, {
-      headers: {
-        'Content-Type': 'application/json',
-        'Authorization': 'Bearer test-token' // 濡傛灉闇�瑕佽璇�
-      }
-    });
-
-    if (response.data.errors) {
-      console.error('GraphQL鏌ヨ鍑洪敊:', response.data.errors);
-      return false;
-    }
-
-    console.log('鉁� GraphQL鏌ヨ鎴愬姛!');
-    console.log('杩斿洖鐨勬暟鎹粨鏋�:', JSON.stringify(response.data.data, null, 2));
-    return true;
-
-  } catch (error) {
-    console.error('鉂� 璇锋眰澶辫触:', error.message);
-    if (error.response) {
-      console.error('鍝嶅簲鐘舵��:', error.response.status);
-      console.error('鍝嶅簲鏁版嵁:', error.response.data);
-    }
-    return false;
-  }
-}
-
-// 杩愯娴嬭瘯
-testGraphQLQuery().then(success => {
-  if (success) {
-    console.log('\n馃帀 灏忕▼搴忕GraphQL鏌ヨ淇鎴愬姛锛�');
-    console.log('鐜板湪灏忕▼搴忕鏌ヨ鐨勫瓧娈典笌Web绔繚鎸佷竴鑷达紝涓嶅啀鍖呭惈涓嶅瓨鍦ㄧ殑description銆亀eight銆乻ortOrder瀛楁銆�');
-  } else {
-    console.log('\n鉂� 娴嬭瘯澶辫触锛岃妫�鏌ヤ慨澶嶆槸鍚︽纭��');
-  }
-});
\ No newline at end of file
diff --git a/tmp/docx-layout-sample.docx b/tmp/docx-layout-sample.docx
new file mode 100644
index 0000000..68ca0eb
--- /dev/null
+++ b/tmp/docx-layout-sample.docx
Binary files differ
diff --git "a/tmp/docx-\346\265\267\351\200\211-12-new/\133Content_Types\135.xml" "b/tmp/docx-\346\265\267\351\200\211-12-new/\133Content_Types\135.xml"
new file mode 100644
index 0000000..f4911a1
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12-new/\133Content_Types\135.xml"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/><Default ContentType="application/xml" Extension="xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/><Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" PartName="/word/document.xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" PartName="/word/settings.xml"/></Types>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12-new/_rels/.rels" "b/tmp/docx-\346\265\267\351\200\211-12-new/_rels/.rels"
new file mode 100644
index 0000000..e477e23
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12-new/_rels/.rels"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Target="word/document.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"/><Relationship Id="rId2" Target="docProps/app.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"/><Relationship Id="rId3" Target="docProps/core.xml" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"/></Relationships>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12-new/docProps/app.xml" "b/tmp/docx-\346\265\267\351\200\211-12-new/docProps/app.xml"
new file mode 100644
index 0000000..0066519
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12-new/docProps/app.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"><Application>Apache POI</Application></Properties>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12-new/docProps/core.xml" "b/tmp/docx-\346\265\267\351\200\211-12-new/docProps/core.xml"
new file mode 100644
index 0000000..28da7d3
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12-new/docProps/core.xml"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dcterms:created xsi:type="dcterms:W3CDTF">2025-11-06T05:16:40Z</dcterms:created><dc:creator>Apache POI</dc:creator></cp:coreProperties>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12-new/word/_rels/document.xml.rels" "b/tmp/docx-\346\265\267\351\200\211-12-new/word/_rels/document.xml.rels"
new file mode 100644
index 0000000..4ec4c54
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12-new/word/_rels/document.xml.rels"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Target="settings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"/></Relationships>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12-new/word/document.xml" "b/tmp/docx-\346\265\267\351\200\211-12-new/word/document.xml"
new file mode 100644
index 0000000..15fa295
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12-new/word/document.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:pPr><w:jc w:val="center"/></w:pPr><w:r><w:rPr><w:b w:val="on"/><w:sz w:val="36"/></w:rPr><w:t>璇勫璇勫垎琛�</w:t></w:r></w:p><w:p><w:r><w:t>娲诲姩锛氭櫤鎱ф櫤鑳介」鐩粍    闃舵锛氭捣閫�</w:t></w:r></w:p><w:p><w:r><w:t>椤圭洰锛�12    閫夋墜锛氬尶鍚嶇敤鎴�</w:t></w:r></w:p><w:p><w:r><w:rPr><w:i w:val="on"/></w:rPr><w:t>灏氭棤璇勫垎锛堜互涓嬩负璇勫垎椤规ā鏉匡級</w:t></w:r></w:p><w:tbl><w:tblPr><w:tblW w:w="9072" w:type="dxa"/><w:tblBorders><w:top w:val="single" w:sz="8" w:color="000000"/><w:left w:val="single" w:sz="8" w:color="000000"/><w:bottom w:val="single" w:sz="8" w:color="000000"/><w:right w:val="single" w:sz="8" w:color="000000"/><w:insideH w:val="single" w:sz="8" w:color="000000"/><w:insideV w:val="single" w:sz="8" w:color="000000"/></w:tblBorders></w:tblPr><w:tblGrid><w:gridCol w:w="6072"/><w:gridCol w:w="1500"/><w:gridCol w:w="1500"/></w:tblGrid><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>璇勫垎椤�</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>寰楀垎</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>婊″垎</w:t></w:r></w:p></w:tc></w:tr><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>鍚圭摱瀛�</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>-</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>25</w:t></w:r></w:p></w:tc></w:tr><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>鐡舵暟</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>-</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>25</w:t></w:r></w:p></w:tc></w:tr><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>閰掑搧</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>-</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>50</w:t></w:r></w:p></w:tc></w:tr></w:tbl><w:p><w:pPr><w:spacing w:before="200"/></w:pPr></w:p><w:tbl><w:tblPr><w:tblW w:w="9072" w:type="dxa"/><w:tblBorders><w:top w:val="single" w:sz="8" w:color="000000"/><w:left w:val="single" w:sz="8" w:color="000000"/><w:bottom w:val="single" w:sz="8" w:color="000000"/><w:right w:val="single" w:sz="8" w:color="000000"/><w:insideH w:val="single" w:sz="8" w:color="000000"/><w:insideV w:val="single" w:sz="8" w:color="000000"/></w:tblBorders></w:tblPr><w:tblGrid><w:gridCol w:w="6500"/><w:gridCol w:w="2572"/></w:tblGrid><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>涓撳璇勫绛惧瓧锛歘______________</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/><w:rFonts w:ascii="瀹嬩綋" w:hAnsi="瀹嬩綋" w:cs="瀹嬩綋" w:eastAsia="瀹嬩綋"/></w:rPr><w:t>绛惧瓧鏃ユ湡锛歘_________</w:t></w:r></w:p></w:tc></w:tr></w:tbl><w:sectPr><w:pgSz w:w="11907" w:h="16840"/><w:pgMar w:left="1440" w:right="1440" w:top="1440" w:bottom="1440"/></w:sectPr></w:body></w:document>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12-new/word/settings.xml" "b/tmp/docx-\346\265\267\351\200\211-12-new/word/settings.xml"
new file mode 100644
index 0000000..ebf5750
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12-new/word/settings.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"/>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12/\133Content_Types\135.xml" "b/tmp/docx-\346\265\267\351\200\211-12/\133Content_Types\135.xml"
new file mode 100644
index 0000000..f4911a1
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12/\133Content_Types\135.xml"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/><Default ContentType="application/xml" Extension="xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/><Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" PartName="/word/document.xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" PartName="/word/settings.xml"/></Types>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12/_rels/.rels" "b/tmp/docx-\346\265\267\351\200\211-12/_rels/.rels"
new file mode 100644
index 0000000..e477e23
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12/_rels/.rels"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Target="word/document.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"/><Relationship Id="rId2" Target="docProps/app.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"/><Relationship Id="rId3" Target="docProps/core.xml" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"/></Relationships>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12/docProps/app.xml" "b/tmp/docx-\346\265\267\351\200\211-12/docProps/app.xml"
new file mode 100644
index 0000000..0066519
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12/docProps/app.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"><Application>Apache POI</Application></Properties>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12/docProps/core.xml" "b/tmp/docx-\346\265\267\351\200\211-12/docProps/core.xml"
new file mode 100644
index 0000000..94b9fd8
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12/docProps/core.xml"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dcterms:created xsi:type="dcterms:W3CDTF">2025-11-06T03:40:51Z</dcterms:created><dc:creator>Apache POI</dc:creator></cp:coreProperties>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12/word/_rels/document.xml.rels" "b/tmp/docx-\346\265\267\351\200\211-12/word/_rels/document.xml.rels"
new file mode 100644
index 0000000..4ec4c54
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12/word/_rels/document.xml.rels"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Target="settings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"/></Relationships>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12/word/document.xml" "b/tmp/docx-\346\265\267\351\200\211-12/word/document.xml"
new file mode 100644
index 0000000..8df0fba
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12/word/document.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:pPr><w:jc w:val="center"/></w:pPr><w:r><w:rPr><w:b w:val="on"/><w:sz w:val="36"/></w:rPr><w:t>璇勫璇勫垎琛�</w:t></w:r></w:p><w:p><w:r><w:t>娲诲姩锛氭櫤鎱ф櫤鑳介」鐩粍    闃舵锛氭捣閫�</w:t></w:r></w:p><w:p><w:r><w:t>椤圭洰锛�12    閫夋墜锛氬尶鍚嶇敤鎴�</w:t></w:r></w:p><w:p><w:r><w:t>灏氭棤璇勫垎</w:t></w:r></w:p></w:body></w:document>
\ No newline at end of file
diff --git "a/tmp/docx-\346\265\267\351\200\211-12/word/settings.xml" "b/tmp/docx-\346\265\267\351\200\211-12/word/settings.xml"
new file mode 100644
index 0000000..ebf5750
--- /dev/null
+++ "b/tmp/docx-\346\265\267\351\200\211-12/word/settings.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"/>
\ No newline at end of file
diff --git a/tmp/review-0/README.txt b/tmp/review-0/README.txt
new file mode 100644
index 0000000..0fab9d1
--- /dev/null
+++ b/tmp/review-0/README.txt
@@ -0,0 +1,4 @@
+钃夋槗鍒涜瘎瀹″鍑�
+娲诲姩: 鏅烘収鏅鸿兘椤圭洰缁� (ID=74)
+瀵煎嚭鏃堕棿: 2025-11-06T11:56:25.701008300
+娲诲姩鎶ュ悕浜烘暟: 5
diff --git "a/tmp/review-0/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx" "b/tmp/review-0/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
new file mode 100644
index 0000000..9e1a899
--- /dev/null
+++ "b/tmp/review-0/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
Binary files differ
diff --git a/tmp/review-1/README.txt b/tmp/review-1/README.txt
new file mode 100644
index 0000000..fe713f3
--- /dev/null
+++ b/tmp/review-1/README.txt
@@ -0,0 +1,5 @@
+钃夋槗鍒涜瘎瀹″鍑�
+娲诲姩: 鏅烘収鏅鸿兘椤圭洰缁� (ID=74)
+瀵煎嚭鏃堕棿: 2025-11-06T11:57:45.558184200
+闃舵ID: [76]
+闃舵 76 鎶ュ悕浜烘暟: 0
diff --git a/tmp/review-2/README.txt b/tmp/review-2/README.txt
new file mode 100644
index 0000000..c485535
--- /dev/null
+++ b/tmp/review-2/README.txt
@@ -0,0 +1,5 @@
+钃夋槗鍒涜瘎瀹″鍑�
+娲诲姩: 鏅烘収鏅鸿兘椤圭洰缁� (ID=74)
+瀵煎嚭鏃堕棿: 2025-11-06T11:40:51.537055800
+闃舵ID: [75]
+闃舵 75 鎶ュ悕浜烘暟: 5
diff --git "a/tmp/review-2/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx" "b/tmp/review-2/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
new file mode 100644
index 0000000..a0a7177
--- /dev/null
+++ "b/tmp/review-2/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
Binary files differ
diff --git a/tmp/review-export-2/README.txt b/tmp/review-export-2/README.txt
new file mode 100644
index 0000000..a3706a0
--- /dev/null
+++ b/tmp/review-export-2/README.txt
@@ -0,0 +1,7 @@
+钃夋槗鍒涜瘎瀹″鍑�
+娲诲姩: 鏅烘収鏅鸿兘椤圭洰缁� (ID=74)
+瀵煎嚭鏃堕棿: 2025-11-06T13:16:40.539277500
+闃舵ID: [75, 76, 77]
+闃舵 75 鎶ュ悕浜烘暟: 5
+闃舵 76 鎶ュ悕浜烘暟: 0
+闃舵 77 鎶ュ悕浜烘暟: 0
diff --git "a/tmp/review-export-2/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx" "b/tmp/review-export-2/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
new file mode 100644
index 0000000..9e0df2a
--- /dev/null
+++ "b/tmp/review-export-2/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
Binary files differ
diff --git a/tmp/review-export/README.txt b/tmp/review-export/README.txt
new file mode 100644
index 0000000..b1c4d66
--- /dev/null
+++ b/tmp/review-export/README.txt
@@ -0,0 +1,7 @@
+钃夋槗鍒涜瘎瀹″鍑�
+娲诲姩: 鏅烘収鏅鸿兘椤圭洰缁� (ID=74)
+瀵煎嚭鏃堕棿: 2025-11-06T13:03:21.174946400
+闃舵ID: [75, 76, 77]
+闃舵 75 鎶ュ悕浜烘暟: 5
+闃舵 76 鎶ュ悕浜烘暟: 0
+闃舵 77 鎶ュ悕浜烘暟: 0
diff --git "a/tmp/review-export/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx" "b/tmp/review-export/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
new file mode 100644
index 0000000..c1e79cc
--- /dev/null
+++ "b/tmp/review-export/players/\346\265\267\351\200\211/12-\345\214\277\345\220\215\347\224\250\346\210\267.docx"
Binary files differ
diff --git "a/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/\133Content_Types\135.xml" "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/\133Content_Types\135.xml"
new file mode 100644
index 0000000..f4911a1
--- /dev/null
+++ "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/\133Content_Types\135.xml"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/><Default ContentType="application/xml" Extension="xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/><Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" PartName="/word/document.xml"/><Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" PartName="/word/settings.xml"/></Types>
\ No newline at end of file
diff --git "a/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/_rels/.rels" "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/_rels/.rels"
new file mode 100644
index 0000000..e477e23
--- /dev/null
+++ "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/_rels/.rels"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Target="word/document.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"/><Relationship Id="rId2" Target="docProps/app.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"/><Relationship Id="rId3" Target="docProps/core.xml" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"/></Relationships>
\ No newline at end of file
diff --git "a/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/docProps/app.xml" "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/docProps/app.xml"
new file mode 100644
index 0000000..0066519
--- /dev/null
+++ "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/docProps/app.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"><Application>Apache POI</Application></Properties>
\ No newline at end of file
diff --git "a/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/docProps/core.xml" "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/docProps/core.xml"
new file mode 100644
index 0000000..c994295
--- /dev/null
+++ "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/docProps/core.xml"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dcterms:created xsi:type="dcterms:W3CDTF">2025-11-06T05:03:21Z</dcterms:created><dc:creator>Apache POI</dc:creator></cp:coreProperties>
\ No newline at end of file
diff --git "a/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/_rels/document.xml.rels" "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/_rels/document.xml.rels"
new file mode 100644
index 0000000..4ec4c54
--- /dev/null
+++ "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/_rels/document.xml.rels"
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Target="settings.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings"/></Relationships>
\ No newline at end of file
diff --git "a/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/document.xml" "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/document.xml"
new file mode 100644
index 0000000..45dc992
--- /dev/null
+++ "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/document.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:pPr><w:jc w:val="center"/></w:pPr><w:r><w:rPr><w:b w:val="on"/><w:sz w:val="36"/></w:rPr><w:t>璇勫璇勫垎琛�</w:t></w:r></w:p><w:p><w:r><w:t>娲诲姩锛氭櫤鎱ф櫤鑳介」鐩粍    闃舵锛氭捣閫�</w:t></w:r></w:p><w:p><w:r><w:t>椤圭洰锛�12    閫夋墜锛氬尶鍚嶇敤鎴�</w:t></w:r></w:p><w:p><w:r><w:rPr><w:i w:val="on"/></w:rPr><w:t>灏氭棤璇勫垎锛堜互涓嬩负璇勫垎椤规ā鏉匡級</w:t></w:r></w:p><w:tbl><w:tblPr><w:tblW w:w="9072" w:type="dxa"/><w:tblBorders><w:top w:val="single" w:sz="8" w:color="000000"/><w:left w:val="single" w:sz="8" w:color="000000"/><w:bottom w:val="single" w:sz="8" w:color="000000"/><w:right w:val="single"/><w:insideH w:val="single" w:sz="8" w:color="000000"/><w:insideV w:val="single" w:sz="8" w:color="000000"/></w:tblBorders></w:tblPr><w:tblGrid><w:gridCol w:w="6072"/><w:gridCol w:w="1500"/><w:gridCol w:w="1500"/></w:tblGrid><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>璇勫垎椤�</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>寰楀垎</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>婊″垎</w:t></w:r></w:p></w:tc></w:tr><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>鍚圭摱瀛�</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>-</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>25</w:t></w:r></w:p></w:tc></w:tr><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>鐡舵暟</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>-</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>25</w:t></w:r></w:p></w:tc></w:tr><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>閰掑搧</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>-</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>50</w:t></w:r></w:p></w:tc></w:tr></w:tbl><w:p><w:pPr><w:spacing w:before="200"/></w:pPr></w:p><w:tbl><w:tblPr><w:tblW w:w="9072" w:type="dxa"/><w:tblBorders><w:top w:val="single" w:sz="8" w:color="000000"/><w:left w:val="single" w:sz="8" w:color="000000"/><w:bottom w:val="single" w:sz="8" w:color="000000"/><w:right w:val="single"/><w:insideH w:val="single" w:sz="8" w:color="000000"/><w:insideV w:val="single" w:sz="8" w:color="000000"/></w:tblBorders></w:tblPr><w:tblGrid><w:gridCol w:w="6500"/><w:gridCol w:w="2572"/></w:tblGrid><w:tr><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>涓撳璇勫绛惧瓧锛歘______________</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:type="dxa"/></w:tcPr><w:p><w:pPr><w:jc w:val="left"/></w:pPr><w:r><w:rPr><w:sz w:val="24"/></w:rPr><w:t>绛惧瓧鏃ユ湡锛歘_________</w:t></w:r></w:p></w:tc></w:tr></w:tbl></w:body></w:document>
\ No newline at end of file
diff --git "a/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/settings.xml" "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/settings.xml"
new file mode 100644
index 0000000..ebf5750
--- /dev/null
+++ "b/tmp/review-export/players/\346\265\267\351\200\211/docx-xml/word/settings.xml"
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"/>
\ No newline at end of file
diff --git a/update-activity-times.js b/update-activity-times.js
deleted file mode 100644
index 6d7fcf5..0000000
--- a/update-activity-times.js
+++ /dev/null
@@ -1,55 +0,0 @@
-const mysql = require('mysql2/promise');
-const fs = require('fs');
-
-async function updateActivityTimes() {
-    const connection = await mysql.createConnection({
-        host: '139.155.104.10',
-        port: 3306,
-        user: 'root',
-        password: 'Xml@uk2025',
-        database: 'ryc',
-        authPlugins: {
-            mysql_native_password: () => () => Buffer.alloc(0)
-        }
-    });
-
-    try {
-        console.log('杩炴帴鏁版嵁搴撴垚鍔�');
-
-        // 璇诲彇SQL鏂囦欢
-        const sqlContent = fs.readFileSync('./update-activity-times.sql', 'utf8');
-        
-        // 鍒嗗壊SQL璇彞
-        const sqlStatements = sqlContent
-            .split(';')
-            .map(stmt => stmt.trim())
-            .filter(stmt => stmt && !stmt.startsWith('--'));
-
-        console.log(`鍑嗗鎵ц ${sqlStatements.length} 鏉QL璇彞`);
-
-        // 鎵ц姣忔潯SQL璇彞
-        for (let i = 0; i < sqlStatements.length; i++) {
-            const sql = sqlStatements[i];
-            if (sql) {
-                console.log(`鎵цSQL ${i + 1}: ${sql.substring(0, 50)}...`);
-                const [result] = await connection.execute(sql);
-                
-                if (sql.toLowerCase().startsWith('select')) {
-                    console.log('鏌ヨ缁撴灉:', result);
-                } else {
-                    console.log(`褰卞搷琛屾暟: ${result.affectedRows}`);
-                }
-            }
-        }
-
-        console.log('鎵�鏈塖QL璇彞鎵ц瀹屾垚');
-
-    } catch (error) {
-        console.error('鎵ц澶辫触:', error);
-    } finally {
-        await connection.end();
-        console.log('鏁版嵁搴撹繛鎺ュ凡鍏抽棴');
-    }
-}
-
-updateActivityTimes();
\ No newline at end of file
diff --git a/update-activity-times.sql b/update-activity-times.sql
deleted file mode 100644
index 10c1c81..0000000
--- a/update-activity-times.sql
+++ /dev/null
@@ -1,65 +0,0 @@
--- 鏇存柊Activity琛ㄤ腑鐨勬椂闂存暟鎹紝涓烘瘡涓瘮璧涜缃笉鍚岀殑鏃堕棿
--- 杩欎釜鑴氭湰灏嗕负涓嶅悓鐨勬瘮璧涜缃笉鍚岀殑鎶ュ悕鎴鏃堕棿鍜屾瘮璧涙椂闂�
-
--- 鏇存柊姣旇禌ID 16 (2011)
-UPDATE t_activity SET 
-    signup_deadline = '2025-01-15 18:00:00',
-    match_time = '2025-01-20 09:00:00'
-WHERE id = 16;
-
--- 鏇存柊姣旇禌ID 19 (2012)
-UPDATE t_activity SET 
-    signup_deadline = '2025-01-20 18:00:00',
-    match_time = '2025-01-25 09:00:00'
-WHERE id = 19;
-
--- 鏇存柊姣旇禌ID 22 (2013)
-UPDATE t_activity SET 
-    signup_deadline = '2025-01-25 18:00:00',
-    match_time = '2025-01-30 09:00:00'
-WHERE id = 22;
-
--- 鏇存柊姣旇禌ID 25 (2020)
-UPDATE t_activity SET 
-    signup_deadline = '2025-02-01 18:00:00',
-    match_time = '2025-02-05 09:00:00'
-WHERE id = 25;
-
--- 鏇存柊姣旇禌ID 28 (2021)
-UPDATE t_activity SET 
-    signup_deadline = '2025-02-05 18:00:00',
-    match_time = '2025-02-10 09:00:00'
-WHERE id = 28;
-
--- 鏇存柊姣旇禌ID 31 (2030)
-UPDATE t_activity SET 
-    signup_deadline = '2025-02-10 18:00:00',
-    match_time = '2025-02-15 09:00:00'
-WHERE id = 31;
-
--- 鏇存柊姣旇禌ID 34 (2031)
-UPDATE t_activity SET 
-    signup_deadline = '2025-02-15 18:00:00',
-    match_time = '2025-02-20 09:00:00'
-WHERE id = 34;
-
--- 鏇存柊姣旇禌ID 37 (3001)
-UPDATE t_activity SET 
-    signup_deadline = '2025-02-20 18:00:00',
-    match_time = '2025-02-25 09:00:00'
-WHERE id = 37;
-
--- 鏇存柊姣旇禌ID 40 (3002)
-UPDATE t_activity SET 
-    signup_deadline = '2025-02-25 18:00:00',
-    match_time = '2025-03-01 09:00:00'
-WHERE id = 40;
-
--- 鏇存柊姣旇禌ID 43 (3003)
-UPDATE t_activity SET 
-    signup_deadline = '2025-03-01 18:00:00',
-    match_time = '2025-03-05 09:00:00'
-WHERE id = 43;
-
--- 楠岃瘉鏇存柊缁撴灉
-SELECT id, name, signup_deadline, match_time FROM t_activity WHERE id IN (16, 19, 22, 25, 28, 31, 34, 37, 40, 43) ORDER BY id;
\ No newline at end of file
diff --git a/verify-user-judge-mapping.js b/verify-user-judge-mapping.js
deleted file mode 100644
index e69db8c..0000000
--- a/verify-user-judge-mapping.js
+++ /dev/null
@@ -1,156 +0,0 @@
-const mysql = require('mysql2/promise');
-
-// 浠巃pplication.yml璇诲彇鐨勬暟鎹簱閰嶇疆
-const dbConfig = {
-    host: '139.155.104.10',
-    port: 3306,
-    user: 'ryc',
-    password: 'KiYap3E8X8RLcM6T',
-    database: 'ryc',
-    connectTimeout: 60000,
-    acquireTimeout: 60000,
-    timeout: 60000
-};
-
-async function verifyUserJudgeMapping() {
-    console.log('=== 楠岃瘉鐢ㄦ埛ID=152鐨勮瘎濮旀潈闄� ===\n');
-    
-    let connection;
-    try {
-        // 杩炴帴鏁版嵁搴�
-        console.log('姝e湪杩炴帴鏁版嵁搴�...');
-        connection = await mysql.createConnection(dbConfig);
-        console.log('鉁� 鏁版嵁搴撹繛鎺ユ垚鍔焅n');
-        
-        // 1. 鏌ヨ鐢ㄦ埛ID=152鐨勫熀鏈俊鎭�
-        console.log('1. 鏌ヨ鐢ㄦ埛ID=152鐨勫熀鏈俊鎭�:');
-        const [userRows] = await connection.execute(
-            'SELECT id, name, phone, state, create_time FROM t_user WHERE id = ?',
-            [152]
-        );
-        
-        if (userRows.length === 0) {
-            console.log('鉂� 鐢ㄦ埛ID=152涓嶅瓨鍦�');
-            return;
-        }
-        
-        const user = userRows[0];
-        console.log('鉁� 鐢ㄦ埛淇℃伅:');
-        console.log(`   ID: ${user.id}`);
-        console.log(`   濮撳悕: ${user.name}`);
-        console.log(`   鎵嬫満鍙�: ${user.phone}`);
-        console.log(`   鐘舵��: ${user.state}`);
-        console.log(`   鍒涘缓鏃堕棿: ${user.create_time}`);
-        
-        // 2. 鏌ヨ璇ョ敤鎴峰搴旂殑璇勫璁板綍
-        console.log('\n2. 鏌ヨ鐢ㄦ埛ID=152瀵瑰簲鐨勮瘎濮旇褰�:');
-        const [judgeRows] = await connection.execute(
-            'SELECT id, name, user_id, phone, state, title, company, create_time FROM t_judge WHERE user_id = ?',
-            [152]
-        );
-        
-        if (judgeRows.length === 0) {
-            console.log('鉂� 鐢ㄦ埛ID=152娌℃湁瀵瑰簲鐨勮瘎濮旇褰�');
-            
-            // 妫�鏌ユ槸鍚︽湁鍚屾墜鏈哄彿鐨勮瘎濮旇褰�
-            console.log('\n3. 妫�鏌ユ槸鍚︽湁鍚屾墜鏈哄彿鐨勮瘎濮旇褰�:');
-            const [judgeByPhoneRows] = await connection.execute(
-                'SELECT id, name, user_id, phone, state FROM t_judge WHERE phone = ?',
-                [user.phone]
-            );
-            
-            if (judgeByPhoneRows.length > 0) {
-                console.log('鈿狅笍  鍙戠幇鍚屾墜鏈哄彿鐨勮瘎濮旇褰�:');
-                judgeByPhoneRows.forEach(judge => {
-                    console.log(`   璇勫ID: ${judge.id}, 鐢ㄦ埛ID: ${judge.user_id}, 濮撳悕: ${judge.name}, 鐘舵��: ${judge.state}`);
-                });
-            } else {
-                console.log('鉂� 娌℃湁鎵惧埌鍚屾墜鏈哄彿鐨勮瘎濮旇褰�');
-            }
-            return;
-        }
-        
-        const judge = judgeRows[0];
-        console.log('鉁� 璇勫淇℃伅:');
-        console.log(`   璇勫ID: ${judge.id}`);
-        console.log(`   鐢ㄦ埛ID: ${judge.user_id}`);
-        console.log(`   濮撳悕: ${judge.name}`);
-        console.log(`   鎵嬫満鍙�: ${judge.phone}`);
-        console.log(`   鐘舵��: ${judge.state}`);
-        console.log(`   鑱屼綅: ${judge.title || '鏈缃�'}`);
-        console.log(`   鍏徃: ${judge.company || '鏈缃�'}`);
-        console.log(`   鍒涘缓鏃堕棿: ${judge.create_time}`);
-        
-        // 楠岃瘉鏄惁鏄瘎濮擨D=72
-        if (judge.id === 72) {
-            console.log('鉁� 纭锛氱敤鎴稩D=152瀵瑰簲璇勫ID=72');
-        } else {
-            console.log(`鈿狅笍  娉ㄦ剰锛氱敤鎴稩D=152瀵瑰簲璇勫ID=${judge.id}锛屼笉鏄�72`);
-        }
-        
-        // 3. 妫�鏌ヨ瘎濮旂姸鎬�
-        console.log('\n3. 妫�鏌ヨ瘎濮旂姸鎬�:');
-        if (judge.state === 1) {
-            console.log('鉁� 璇勫鐘舵�佹甯革紙state=1锛�');
-        } else {
-            console.log(`鉂� 璇勫鐘舵�佸紓甯革紙state=${judge.state}锛塦);
-        }
-        
-        // 4. 鏌ヨ璇勫鍙備笌鐨勬椿鍔�
-        console.log('\n4. 鏌ヨ璇勫鍙備笌鐨勬椿鍔�:');
-        const [activityJudgeRows] = await connection.execute(
-            'SELECT aj.activity_id, a.name as activity_name, aj.stage_id FROM t_activity_judge aj LEFT JOIN t_activity a ON aj.activity_id = a.id WHERE aj.judge_id = ? LIMIT 5',
-            [judge.id]
-        );
-        
-        if (activityJudgeRows.length > 0) {
-            console.log('鉁� 璇勫鍙備笌鐨勬椿鍔�:');
-            activityJudgeRows.forEach(activity => {
-                console.log(`   娲诲姩ID: ${activity.activity_id}, 娲诲姩鍚嶇О: ${activity.activity_name || '鏈煡'}, 闃舵ID: ${activity.stage_id}`);
-            });
-        } else {
-            console.log('鈿狅笍  璇勫娌℃湁鍙備笌浠讳綍娲诲姩');
-        }
-        
-        // 5. 鏌ヨ鏈�杩戠殑寰俊鐧诲綍璁板綍
-        console.log('\n5. 鏌ヨ鐢ㄦ埛鏈�杩戠殑寰俊鐧诲綍璁板綍:');
-        const [wxLoginRows] = await connection.execute(
-            'SELECT id, wx_openid, user_id, login_time, phone_authorized FROM t_wx_login_record WHERE user_id = ? ORDER BY login_time DESC LIMIT 3',
-            [152]
-        );
-        
-        if (wxLoginRows.length > 0) {
-            console.log('鉁� 鏈�杩戠殑寰俊鐧诲綍璁板綍:');
-            wxLoginRows.forEach(record => {
-                console.log(`   璁板綍ID: ${record.id}, OpenID: ${record.wx_openid}, 鐧诲綍鏃堕棿: ${record.login_time}, 鎵嬫満鎺堟潈: ${record.phone_authorized}`);
-            });
-        } else {
-            console.log('鈿狅笍  娌℃湁鎵惧埌寰俊鐧诲綍璁板綍');
-        }
-        
-        // 6. 鎬荤粨
-        console.log('\n=== 鎬荤粨 ===');
-        if (judge.state === 1) {
-            console.log('鉁� 鐢ㄦ埛ID=152纭疄鏈夋湁鏁堢殑璇勫鏉冮檺');
-            console.log('鉁� 濡傛灉鍚庣浠嶇劧鎷掔粷璁块棶锛屽彲鑳芥槸UserContextUtil鐨勯�昏緫鏈夐棶棰�');
-        } else {
-            console.log('鉂� 璇勫璁板綍瀛樺湪浣嗙姸鎬佸紓甯革紝杩欏彲鑳芥槸鏉冮檺琚嫆缁濈殑鍘熷洜');
-        }
-        
-    } catch (error) {
-        console.error('鉂� 鏁版嵁搴撴煡璇㈠け璐�:', error.message);
-        console.error('閿欒璇︽儏:', error);
-    } finally {
-        if (connection) {
-            await connection.end();
-            console.log('\n鏁版嵁搴撹繛鎺ュ凡鍏抽棴');
-        }
-    }
-}
-
-// 杩愯楠岃瘉
-if (require.main === module) {
-    verifyUserJudgeMapping();
-}
-
-module.exports = { verifyUserJudgeMapping };
\ No newline at end of file
diff --git a/web/src/api/activity.js b/web/src/api/activity.js
index 8c42f87..ee9ab7a 100644
--- a/web/src/api/activity.js
+++ b/web/src/api/activity.js
@@ -44,6 +44,7 @@
       stateName
       createTime
       updateTime
+      reviewExportUrl
       ratingScheme {
         id
         name
@@ -61,6 +62,7 @@
         state
         stateName
         sortOrder
+        reviewExportUrl
         ratingScheme {
           id
           name
diff --git a/web/src/api/reviewExport.js b/web/src/api/reviewExport.js
new file mode 100644
index 0000000..779aad1
--- /dev/null
+++ b/web/src/api/reviewExport.js
@@ -0,0 +1,70 @@
+import { graphqlRequest } from '@/config/api'
+
+// 瀵煎嚭璇勫ZIP鐨凣raphQL鍙樻洿
+const EXPORT_REVIEW_ZIP_MUTATION = `
+  mutation ExportReviewZip($activityId: ID!, $stageIds: [ID]) {
+    exportReviewZip(activityId: $activityId, stageIds: $stageIds) {
+      success
+      url
+      message
+    }
+  }
+`
+
+// 寮傛瀵煎嚭锛氬惎鍔ㄨ瘎瀹″鍑轰换鍔�
+const START_REVIEW_EXPORT_JOB_MUTATION = `
+  mutation StartReviewExportJob($activityId: ID!, $stageIds: [ID]) {
+    startReviewExportJob(activityId: $activityId, stageIds: $stageIds)
+  }
+`
+
+// 鏌ヨ璇勫瀵煎嚭浠诲姟鐘舵��
+const GET_REVIEW_EXPORT_JOB_STATUS_QUERY = `
+  query GetReviewExportJobStatus($jobId: String!) {
+    getReviewExportJobStatus(jobId: $jobId) {
+      jobId
+      status
+      url
+      message
+      progress
+    }
+  }
+`
+
+/**
+ * 瑙﹀彂璇勫瀵煎嚭ZIP
+ * @param {number|string} activityId 娲诲姩ID锛堜富娲诲姩ID锛�
+ * @param {Array<number>|null} stageIds 鍙�夌殑闃舵ID鍒楄〃锛涜嫢涓虹┖鍒欏鍑烘暣涓椿鍔�
+ * @returns {Promise<{success: boolean, url?: string, message?: string}>}
+ */
+export const exportReviewZip = async (activityId, stageIds = null) => {
+  const result = await graphqlRequest(EXPORT_REVIEW_ZIP_MUTATION, { activityId, stageIds })
+  return result?.data?.exportReviewZip
+}
+
+/**
+ * 鍚姩璇勫瀵煎嚭浠诲姟锛岃繑鍥� jobId
+ * @param {number|string} activityId
+ * @param {Array<number>|null} stageIds
+ * @returns {Promise<string|null>} jobId
+ */
+export const startReviewExportJob = async (activityId, stageIds = null) => {
+  const result = await graphqlRequest(START_REVIEW_EXPORT_JOB_MUTATION, { activityId, stageIds })
+  return result?.data?.startReviewExportJob || null
+}
+
+/**
+ * 鏌ヨ璇勫瀵煎嚭浠诲姟鐘舵��
+ * @param {string} jobId
+ * @returns {Promise<{jobId: string, status: string, url?: string, message?: string, progress?: number} | null>}
+ */
+export const getReviewExportJobStatus = async (jobId) => {
+  const result = await graphqlRequest(GET_REVIEW_EXPORT_JOB_STATUS_QUERY, { jobId })
+  return result?.data?.getReviewExportJobStatus || null
+}
+
+export default {
+  exportReviewZip,
+  startReviewExportJob,
+  getReviewExportJobStatus
+}
\ No newline at end of file
diff --git a/web/src/views/ActivityDetail.vue b/web/src/views/ActivityDetail.vue
index 886b867..61575e3 100644
--- a/web/src/views/ActivityDetail.vue
+++ b/web/src/views/ActivityDetail.vue
@@ -5,6 +5,7 @@
         <div class="card-header">
           <span>姣旇禌璇︽儏</span>
           <div>
+            <el-button v-if="canExport" type="primary" :loading="exportingActivity" @click="handleExportActivity">瀵煎嚭璇勫ZIP</el-button>
             <el-button @click="handleEdit">缂栬緫姣旇禌</el-button>
             <el-button @click="goBack">杩斿洖</el-button>
           </div>
@@ -23,6 +24,18 @@
           <el-descriptions-item label="姣旇禌鍦板潃">{{ activity.address || '-' }}</el-descriptions-item>
           <el-descriptions-item label="璇勫垎妯℃澘">
             {{ activity.ratingScheme ? activity.ratingScheme.name : '-' }}
+          </el-descriptions-item>
+          <el-descriptions-item label="鏈�杩戣瘎瀹″鍑�" :span="2">
+            <template v-if="activity.reviewExportUrl">
+              <a :href="activity.reviewExportUrl" target="_blank">鐐瑰嚮涓嬭浇</a>
+            </template>
+            <template v-else>
+              鏆傛棤
+            </template>
+            <span v-if="exportJobActivity && exportingActivity" class="export-status">
+              瀵煎嚭浠诲姟杩涜涓�
+              <span v-if="exportJobActivity.progress !== null && exportJobActivity.progress !== undefined">锛坽{ exportJobActivity.progress }}%锛�</span>
+            </span>
           </el-descriptions-item>
           <el-descriptions-item label="鍒涘缓鏃堕棿" :span="2">{{ formatDateTime(activity.createTime) }}</el-descriptions-item>
         </el-descriptions>
@@ -54,11 +67,32 @@
                 <el-tag :type="getStateType(row.state)">{{ row.stateName }}</el-tag>
               </template>
             </el-table-column>
-            <el-table-column label="鎿嶄綔" width="220" fixed="right">
+            <el-table-column label="鏈�杩戝鍑�" width="200">
+              <template #default="{ row }">
+                <template v-if="row.reviewExportUrl">
+                  <a :href="row.reviewExportUrl" target="_blank">涓嬭浇ZIP</a>
+                </template>
+                <template v-else>
+                  <span style="color: #909399;">鏆傛棤</span>
+                </template>
+                <div v-if="exportJobStageMap[row.id] && exportingStageId === row.id" class="export-status">
+                  瀵煎嚭浠诲姟杩涜涓�
+                  <span v-if="exportJobStageMap[row.id].progress !== null && exportJobStageMap[row.id].progress !== undefined">锛坽{ exportJobStageMap[row.id].progress }}%锛�</span>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔" width="320" 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>
                 <el-button size="small" type="danger" @click="deleteStage(row)">鍒犻櫎</el-button>
+                <el-button 
+                  v-if="canExport" 
+                  size="small" 
+                  type="primary" 
+                  :loading="exportingStageId === row.id" 
+                  @click="handleExportStage(row)"
+                >瀵煎嚭闃舵璇勫</el-button>
               </template>
             </el-table-column>
           </el-table>
@@ -125,10 +159,12 @@
 </template>
 
 <script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, onUnmounted, computed } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { getActivity } from '@/api/activity'
+import { exportReviewZip, startReviewExportJob, getReviewExportJobStatus } from '@/api/reviewExport'
+import { isEmployee } from '@/utils/auth'
 
 const router = useRouter()
 const route = useRoute()
@@ -138,6 +174,13 @@
 const activity = ref(null)
 const stageDialogVisible = ref(false)
 const selectedStage = ref(null)
+const exportingActivity = ref(false)
+const exportingStageId = ref(null)
+// 寮傛瀵煎嚭浠诲姟鐘舵��
+const exportJobActivity = ref(null) // { jobId, status, progress, message }
+const exportJobStageMap = ref({}) // { [stageId]: { jobId, status, progress, message } }
+let activityPollTimer = null
+const stagePollTimers = {}
 
 // 璁$畻灞炴��
 const sortedStages = computed(() => {
@@ -248,6 +291,139 @@
 onMounted(() => {
   loadActivity()
 })
+
+onUnmounted(() => {
+  if (activityPollTimer) {
+    clearInterval(activityPollTimer)
+    activityPollTimer = null
+  }
+  Object.keys(stagePollTimers).forEach((sid) => {
+    if (stagePollTimers[sid]) {
+      clearInterval(stagePollTimers[sid])
+      delete stagePollTimers[sid]
+    }
+  })
+})
+
+// 鏄惁鍙墽琛屽鍑猴紙浠呭憳宸ワ級
+const canExport = computed(() => isEmployee())
+
+// 瀵煎嚭涓绘椿鍔ㄨ瘎瀹IP
+const handleExportActivity = async () => {
+  try {
+    await ElMessageBox.confirm('灏嗗鍑鸿姣旇禌涓嬫墍鏈夐樁娈电殑宸茶瘎鍒嗘暟鎹负ZIP锛岀‘璁ょ户缁紵', '纭瀵煎嚭', {
+      confirmButtonText: '纭畾',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    })
+    exportingActivity.value = true
+    // 浼樺厛浣跨敤寮傛瀵煎嚭浠诲姟
+    const jobId = await startReviewExportJob(route.params.id)
+    if (jobId) {
+      exportJobActivity.value = { jobId, status: 'PENDING', progress: 0 }
+      // 杞浠诲姟鐘舵��
+      activityPollTimer = setInterval(async () => {
+        try {
+          const status = await getReviewExportJobStatus(jobId)
+          if (status) {
+            exportJobActivity.value = status
+            if (status.status === 'SUCCEEDED') {
+              clearInterval(activityPollTimer)
+              activityPollTimer = null
+              ElMessage.success('瀵煎嚭鎴愬姛')
+              await loadActivity()
+              exportingActivity.value = false
+            } else if (status.status === 'FAILED') {
+              clearInterval(activityPollTimer)
+              activityPollTimer = null
+              ElMessage.error(status.message || '瀵煎嚭澶辫触')
+              exportingActivity.value = false
+            }
+          }
+        } catch (e) {
+          console.warn('鏌ヨ瀵煎嚭浠诲姟鐘舵�佸け璐ワ細', e?.message || e)
+        }
+      }, 2000)
+    } else {
+      // 鍥為��锛氫娇鐢ㄥ悓姝ュ鍑�
+      const res = await exportReviewZip(route.params.id)
+      if (res?.success) {
+        ElMessage.success('瀵煎嚭鎴愬姛')
+        await loadActivity()
+      } else {
+        ElMessage.error(res?.message || '瀵煎嚭澶辫触')
+      }
+      exportingActivity.value = false
+    }
+  } catch (e) {
+    // 鐢ㄦ埛鍙栨秷鎴栧紓甯�
+    if (e && e.message) {
+      console.warn('瀵煎嚭鎿嶄綔鍙栨秷鎴栧け璐ワ細', e.message)
+    }
+  } finally {
+    // 寮傛瀵煎嚭鏃剁敱杞鍥炶皟璐熻矗鍏抽棴 loading锛涙澶勪粎鍦ㄦ湭鍚姩浠诲姟鏃堕噸缃�
+    if (!activityPollTimer) {
+      exportingActivity.value = false
+    }
+  }
+}
+
+// 瀵煎嚭鎸囧畾闃舵璇勫ZIP
+const handleExportStage = async (stage) => {
+  try {
+    await ElMessageBox.confirm(`灏嗗鍑恒��${stage.name}銆戦樁娈电殑宸茶瘎鍒嗘暟鎹负ZIP锛岀‘璁ょ户缁紵`, '纭瀵煎嚭', {
+      confirmButtonText: '纭畾',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    })
+    exportingStageId.value = stage.id
+    const jobId = await startReviewExportJob(route.params.id, [stage.id])
+    if (jobId) {
+      exportJobStageMap.value[stage.id] = { jobId, status: 'PENDING', progress: 0 }
+      // 杞闃舵瀵煎嚭鐘舵��
+      stagePollTimers[stage.id] = setInterval(async () => {
+        try {
+          const status = await getReviewExportJobStatus(jobId)
+          if (status) {
+            exportJobStageMap.value[stage.id] = status
+            if (status.status === 'SUCCEEDED') {
+              clearInterval(stagePollTimers[stage.id])
+              delete stagePollTimers[stage.id]
+              ElMessage.success('瀵煎嚭鎴愬姛')
+              await loadActivity()
+              exportingStageId.value = null
+            } else if (status.status === 'FAILED') {
+              clearInterval(stagePollTimers[stage.id])
+              delete stagePollTimers[stage.id]
+              ElMessage.error(status.message || '瀵煎嚭澶辫触')
+              exportingStageId.value = null
+            }
+          }
+        } catch (e) {
+          console.warn('鏌ヨ闃舵瀵煎嚭浠诲姟鐘舵�佸け璐ワ細', e?.message || e)
+        }
+      }, 2000)
+    } else {
+      // 鍥為��锛氬悓姝ュ鍑�
+      const res = await exportReviewZip(route.params.id, [stage.id])
+      if (res?.success) {
+        ElMessage.success('瀵煎嚭鎴愬姛')
+        await loadActivity()
+      } else {
+        ElMessage.error(res?.message || '瀵煎嚭澶辫触')
+      }
+      exportingStageId.value = null
+    }
+  } catch (e) {
+    if (e && e.message) {
+      console.warn('瀵煎嚭闃舵鎿嶄綔鍙栨秷鎴栧け璐ワ細', e.message)
+    }
+  } finally {
+    if (!stagePollTimers[stage.id]) {
+      exportingStageId.value = null
+    }
+  }
+}
 </script>
 
 <style scoped>
diff --git a/web/src/views/activity-list.vue b/web/src/views/activity-list.vue
index 65b8f86..c1eec42 100644
--- a/web/src/views/activity-list.vue
+++ b/web/src/views/activity-list.vue
@@ -54,9 +54,17 @@
             <el-tag :type="getStatusType(row.stateName)">{{ row.stateName }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="鎿嶄綔" width="120" fixed="right" align="center">
+        <el-table-column label="鎿嶄綔" width="180" fixed="right" align="center">
           <template #default="{ row }">
             <div class="table-actions">
+              <el-button
+                text
+                :icon="View"
+                size="small"
+                @click="handleView(row)"
+                class="action-btn view-btn"
+                title="璇︽儏"
+              />
               <el-button
                 text
                 :icon="Edit"
@@ -99,7 +107,7 @@
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { useRouter } from 'vue-router'
 import { getActivities, updateActivityState } from '@/api/activity'
-import { Search, Plus, Edit, Delete } from '@element-plus/icons-vue'
+import { Search, Plus, Edit, Delete, View } from '@element-plus/icons-vue'
 
 console.log('=== activity-list.vue 缁勪欢寮�濮嬪姞杞� ===')
 
@@ -177,6 +185,11 @@
 // 缂栬緫姣旇禌
 const handleEdit = (row: any) => {
   router.push(`/activity/edit/${row.id}`)
+}
+
+// 鏌ョ湅璇︽儏
+const handleView = (row: any) => {
+  router.push(`/activity/${row.id}`)
 }
 
 // 鍒犻櫎姣旇禌
@@ -351,6 +364,16 @@
   background: rgba(245, 108, 108, 0.1) !important;
 }
 
+.view-btn {
+  color: #67C23A;
+}
+
+.view-btn:hover {
+  color: #5daf34;
+  transform: scale(1.2);
+  background: rgba(103, 194, 58, 0.1) !important;
+}
+
 .pagination {
   margin-top: 20px;
   display: flex;
diff --git a/web/vite.config.ts b/web/vite.config.ts
index d0a27bd..e44174f 100644
--- a/web/vite.config.ts
+++ b/web/vite.config.ts
@@ -15,22 +15,11 @@
     open: true,
     proxy: {
       '/api': {
+        // 灏咥PI浠g悊鍥炴湰鍦板悗绔紝渚夸簬瀹屾暣鑱旇皟锛堝悗绔繍琛屽湪 http://127.0.0.1:8080/api锛�
         target: 'http://127.0.0.1:8080',
         changeOrigin: true,
         secure: false,
-        configure: (proxy, options) => {
-          proxy.on('error', (err, req, res) => {
-            console.log('proxy error', err);
-          });
-          proxy.on('proxyReq', (proxyReq, req, res) => {
-            console.log('Sending Request to the Target:', req.method, req.url);
-          });
-          proxy.on('proxyRes', (proxyRes, req, res) => {
-            console.log('Received Response from the Target:', proxyRes.statusCode, req.url);
-          });
-        },
-        // 涓嶉渶瑕侀噸鍐欒矾寰勶紝鍥犱负鍚庣鐨刢ontext-path灏辨槸/api
-        // rewrite: (path) => path.replace(/^\/api/, '/api')
+        // 涓嶉渶瑕侀噸鍐欒矾寰勶紝鍥犱负鍚庣鐨刢ontext-path灏辨槸 /api
       }
     }
   },
diff --git a/wx/pages/activity/detail.js b/wx/pages/activity/detail.js
index 83989e1..38c6a70 100644
--- a/wx/pages/activity/detail.js
+++ b/wx/pages/activity/detail.js
@@ -9,6 +9,10 @@
     myApplication: null,
     buttonDisabled: false,
     buttonText: '鎴戣鎶ュ悕',
+    // 瀵煎嚭鐩稿叧
+    isEmployee: false,
+    exportingActivity: false,
+    exportingStageId: null,
     loading: true,
     error: null
   },
@@ -88,12 +92,16 @@
             }
           }
 
+          // 瑙掕壊锛氭槸鍚﹀憳宸ワ紙涓诲姙鏂�/绠$悊鍛橈級
+          const isEmployee = !!(app.globalData?.userInfo?.employee && app.globalData.userInfo.employee.id)
+
           this.setData({
             activity: res.activity,
             myApplication: myApplication,
             loading: false,
             buttonDisabled: buttonDisabled,
-            buttonText: buttonText
+            buttonText: buttonText,
+            isEmployee: isEmployee
           });
         } else {
           throw new Error('鏈壘鍒版瘮璧涗俊鎭�');
@@ -135,5 +143,108 @@
       return `${startDate} - ${endDate}`;
     }
     return startDate || endDate;
+  },
+
+  // 澶嶅埗閾炬帴鍒板壀璐存澘
+  copyLink(e) {
+    const url = e.currentTarget.dataset.url
+    if (!url) {
+      wx.showToast({ title: '鏆傛棤閾炬帴', icon: 'none' })
+      return
+    }
+    wx.setClipboardData({
+      data: url,
+      success: () => {
+        wx.showToast({ title: '宸插鍒朵笅杞介摼鎺�', icon: 'success' })
+      }
+    })
+  },
+
+  // 鍦╓ebView鍐呮墦寮�閾炬帴锛堢敤浜庨瑙�/涓嬭浇锛�
+  openWebView(e) {
+    const url = e.currentTarget.dataset.url
+    if (!url) {
+      wx.showToast({ title: '鏆傛棤閾炬帴', icon: 'none' })
+      return
+    }
+    wx.navigateTo({
+      url: `/pages/webview/webview?url=${encodeURIComponent(url)}&title=${encodeURIComponent('璇勫瀵煎嚭ZIP')}`
+    })
+  },
+
+  // 瑙﹀彂瀵煎嚭锛堜富娲诲姩锛�
+  async handleExportActivity() {
+    if (!this.data.isEmployee) {
+      wx.showToast({ title: '鏃犳潈闄愭墽琛屽鍑�', icon: 'none' })
+      return
+    }
+    if (!this.data.activityId) {
+      wx.showToast({ title: '鏃犳晥鐨勬椿鍔↖D', icon: 'none' })
+      return
+    }
+    this.setData({ exportingActivity: true })
+    const mutation = `
+      mutation ExportReviewZip($activityId: ID!, $stageIds: [ID]) {
+        exportReviewZip(activityId: $activityId, stageIds: $stageIds) {
+          success
+          url
+          message
+        }
+      }
+    `
+    try {
+      const res = await app.graphqlRequest(mutation, { activityId: this.data.activityId, stageIds: null })
+      const result = res && res.exportReviewZip
+      if (result && result.success) {
+        wx.showToast({ title: '瀵煎嚭鎴愬姛', icon: 'success' })
+        // 鍒锋柊浠ユ樉绀烘渶鏂伴摼鎺�
+        await this.loadActivityDetail(this.data.activityId)
+      } else {
+        wx.showToast({ title: result?.message || '瀵煎嚭澶辫触', icon: 'none' })
+      }
+    } catch (err) {
+      console.error('瀵煎嚭澶辫触:', err)
+      wx.showToast({ title: '瀵煎嚭澶辫触', icon: 'none' })
+    } finally {
+      this.setData({ exportingActivity: false })
+    }
+  },
+
+  // 瑙﹀彂瀵煎嚭锛堥樁娈碉級
+  async handleExportStage(e) {
+    if (!this.data.isEmployee) {
+      wx.showToast({ title: '鏃犳潈闄愭墽琛屽鍑�', icon: 'none' })
+      return
+    }
+    const stageId = e.currentTarget.dataset.stageId
+    if (!stageId) {
+      wx.showToast({ title: '鏃犳晥鐨勯樁娈礗D', icon: 'none' })
+      return
+    }
+    this.setData({ exportingStageId: stageId })
+    const mutation = `
+      mutation ExportReviewZip($activityId: ID!, $stageIds: [ID]) {
+        exportReviewZip(activityId: $activityId, stageIds: $stageIds) {
+          success
+          url
+          message
+        }
+      }
+    `
+    try {
+      const res = await app.graphqlRequest(mutation, { activityId: this.data.activityId, stageIds: [stageId] })
+      const result = res && res.exportReviewZip
+      if (result && result.success) {
+        wx.showToast({ title: '瀵煎嚭鎴愬姛', icon: 'success' })
+        await this.loadActivityDetail(this.data.activityId)
+      } else {
+        wx.showToast({ title: result?.message || '瀵煎嚭澶辫触', icon: 'none' })
+      }
+    } catch (err) {
+      console.error('瀵煎嚭闃舵澶辫触:', err)
+      wx.showToast({ title: '瀵煎嚭澶辫触', icon: 'none' })
+    } finally {
+      this.setData({ exportingStageId: null })
+    }
   }
 });
diff --git a/wx/pages/activity/detail.wxml b/wx/pages/activity/detail.wxml
index d155420..85b3986 100644
--- a/wx/pages/activity/detail.wxml
+++ b/wx/pages/activity/detail.wxml
@@ -34,6 +34,7 @@
             <text class="info-label">姣旇禌寮�濮嬫椂闂�</text>
             <text class="info-value">{{filters.formatDateTime(activity.matchTime)}}</text>
           </view>
+          <!-- 鏈�杩戣瘎瀹″鍑轰俊鎭笉灞曠ず涓嬭浇鍦板潃锛屾暣浣撶Щ闄� -->
         </view>
       </view>
 
@@ -48,6 +49,10 @@
             <view class="timeline-content">
               <view class="stage-name">{{item.name}}</view>
               <view class="stage-date">{{filters.formatDate(item.matchTime)}}</view>
+              <view class="stage-export">
+                <!-- 涓嶅睍绀轰笅杞藉湴鍧�锛屼粎淇濈暀瀵煎嚭鎸夐挳锛堝憳宸ュ彲瑙侊級 -->
+                <button wx:if="{{isEmployee}}" class="mini-btn primary" size="mini" bindtap="handleExportStage" data-stage-id="{{item.id}}" loading="{{exportingStageId === item.id}}">瀵煎嚭闃舵璇勫</button>
+              </view>
             </view>
           </view>
         </view>
@@ -65,6 +70,7 @@
     <!-- 搴曢儴鎿嶄綔鏍� -->
     <view class="footer-actions">
       <button class="register-btn" bindtap="handleRegister" disabled="{{buttonDisabled}}">{{buttonText}}</button>
+      <button wx:if="{{isEmployee}}" class="export-btn" bindtap="handleExportActivity" loading="{{exportingActivity}}">瀵煎嚭璇勫ZIP</button>
     </view>
   </block>
 </view>
\ No newline at end of file
diff --git a/wx/pages/registration/registration.js b/wx/pages/registration/registration.js
index 2180b15..cdaf2bf 100644
--- a/wx/pages/registration/registration.js
+++ b/wx/pages/registration/registration.js
@@ -1140,6 +1140,11 @@
       errors.phone = '璇疯緭鍏ユ纭殑鎵嬫満鍙�';
     }
 
+    // 椤圭洰鍚嶇О涓哄繀濉」
+    if (!formData.projectName || !formData.projectName.trim()) {
+      errors.projectName = '璇疯緭鍏ラ」鐩悕绉�';
+    }
+
     // 淇濇寔鍘熸湁閫昏緫锛氫笉寮哄埗闄勪欢蹇呭~锛屼笉鏍¢獙鎵�灞炲尯鍩熷繀濉�
 
     this.setData({ errors });
@@ -1258,7 +1263,7 @@
         },
         regionId: formData.regionId,
         projectName: formData.projectName,
-        description: formData.description
+        // 椤圭洰鎻忚堪涓嶉噰闆嗭紝涓嶆彁浜ゅ埌鍚庣
       }
       
       // 绗竴姝ワ細鍏堟彁浜ゆ敞鍐屾暟鎹埌鍚庡彴锛岃幏寰楁敞鍐孖D
@@ -1336,7 +1341,6 @@
       },
       regionId: submitData.regionId || null,
       projectName: submitData.projectName || '',
-      description: submitData.description || '',
       attachmentMediaIds: [] // 鍏堜笉浼犻檮浠�
     };
 
diff --git a/wx/pages/registration/registration.wxml b/wx/pages/registration/registration.wxml
index dbc28e8..0bba1f1 100644
--- a/wx/pages/registration/registration.wxml
+++ b/wx/pages/registration/registration.wxml
@@ -157,12 +157,12 @@
       </view>
     </view>
 
-    <!-- 椤圭洰淇℃伅鍗$墖锛堟寜闇�姹傞殣钘忥級 -->
-    <view wx:if="{{false}}" class="card">
+    <!-- 椤圭洰淇℃伅鍗$墖 -->
+    <view class="card">
       <view class="card-title">椤圭洰淇℃伅</view>
       <!-- 椤圭洰鍚嶇О -->
       <view class="form-item {{errors.projectName ? 'error' : ''}}">
-        <text class="label">椤圭洰鍚嶇О</text>
+        <text class="label required">椤圭洰鍚嶇О</text>
         <view class="input-wrapper">
           <input 
             class="input" 
@@ -174,23 +174,6 @@
           />
         </view>
         <text wx:if="{{errors.projectName}}" class="error-text">{{errors.projectName}}</text>
-      </view>
-
-      <!-- 椤圭洰鎻忚堪 -->
-      <view class="form-item vertical-layout {{errors.description ? 'error' : ''}}">
-        <text class="label">椤圭洰鎻忚堪</text>
-        <view class="input-wrapper">
-          <textarea 
-            class="textarea" 
-            placeholder-class="placeholder-class"
-            placeholder="璇疯缁嗘弿杩版偍鐨勯」鐩唴瀹广�佺洰鏍囧拰鐗硅壊"
-            value="{{formData.description}}"
-            data-field="description"
-            bindinput="onInputChange"
-            maxlength="1000"
-          />
-        </view>
-        <text wx:if="{{errors.description}}" class="error-text">{{errors.description}}</text>
       </view>
     </view>
 

--
Gitblit v1.8.0