From 3714621173c606c4c58439ed8941100ce9ddea14 Mon Sep 17 00:00:00 2001
From: Codex Assistant <codex@example.com>
Date: 星期三, 05 十一月 2025 15:10:49 +0800
Subject: [PATCH] bug

---
 wx/pages/judge/review.js                                                               |  124 ++++-
 wx/pages/judge/review.wxss                                                             |   20 
 wx/pages/judge/review.wxml                                                             |    3 
 wx/pages/index/index.wxml                                                              |   14 
 backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java |    6 
 wx/pages/index/index.wxss                                                              |   78 +++
 backend/src/main/java/com/rongyichuang/config/SecurityConfig.java                      |    1 
 web/src/utils/cos.ts                                                                   |  484 ++++++++++++++-------
 backend/src/main/java/com/rongyichuang/config/GraphQLConfig.java                       |    1 
 wx/pages/index/index.js                                                                |  179 +++++++
 web/src/utils/appConfig.js                                                             |   77 ++-
 backend/src/main/resources/graphql/player.graphqls                                     |   11 
 wx/pages/registration/registration.js                                                  |    2 
 web/vite.config.ts                                                                     |   15 
 web/src/views/next-list.vue                                                            |   11 
 wx/app.js                                                                              |   25 
 backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java     |    6 
 web/src/utils/cos-simple.ts                                                            |  192 +++++--
 web/src/api/media.js                                                                   |    3 
 wx/pages/index/index.json                                                              |    2 
 backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java           |    7 
 web/src/api/promotion.js                                                               |   28 
 backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java                |    3 
 backend/src/main/java/com/rongyichuang/player/service/PromotionService.java            |   28 
 wx/pages/profile/profile.js                                                            |    2 
 wx/app.json                                                                            |    2 
 web/src/views/check-detail.vue                                                         |    5 
 backend/src/main/java/com/rongyichuang/auth/util/JwtUtil.java                          |   30 +
 wx/pages/message/message.js                                                            |    5 
 29 files changed, 1,011 insertions(+), 353 deletions(-)

diff --git a/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java b/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java
index c2d9d43..306d039 100644
--- a/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java
+++ b/backend/src/main/java/com/rongyichuang/activity/repository/ActivityRepository.java
@@ -19,8 +19,14 @@
 
     Page<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
 
+    Page<Activity> findByPidAndStateNotOrderByCreateTimeDesc(Long pid, int state, Pageable pageable);
+
+    Page<Activity> findByPidAndStateNotAndNameContainingOrderByCreateTimeDesc(Long pid, int state, String name, Pageable pageable);
+
     List<Activity> findByPidAndStateOrderByCreateTimeAsc(Long pid, int state);
 
+    List<Activity> findByPidAndStateOrderByCreateTimeDesc(Long pid, int state);
+
     List<Activity> findByStateOrderByPidAscNameAsc(int state);
 
     @Query("SELECT a FROM Activity a WHERE a.pid = 0 ORDER BY a.createTime DESC")
diff --git a/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java b/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
index 0a257b0..fb8b6e4 100644
--- a/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
+++ b/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
@@ -72,10 +72,11 @@
                 page = activityRepository.findByPidAndStateOrderByCreateTimeDesc(0L, state, pageable);
             }
         } else if (hasName) {
-            page = activityRepository.findByPidAndNameContainingOrderByCreateTimeDesc(0L, name, pageable);
+            // 褰搒tate涓簄ull浣嗘湁鍚嶇О鎼滅储鏃讹紝闇�瑕佽繃婊ゆ帀宸插垹闄ょ殑姣旇禌锛坰tate != 0锛�
+            page = activityRepository.findByPidAndStateNotAndNameContainingOrderByCreateTimeDesc(0L, 0, name, pageable);
         } else {
-            // 鏌ヨ鎵�鏈変富娲诲姩锛坧id = 0锛�
-            page = activityRepository.findByPidOrderByCreateTimeDesc(0L, pageable);
+            // 褰搒tate涓簄ull鏃讹紝鏌ヨ鎵�鏈夋湭鍒犻櫎鐨勪富娲诲姩锛坧id = 0 涓� state != 0锛�
+            page = activityRepository.findByPidAndStateNotOrderByCreateTimeDesc(0L, 0, pageable);
         }
 
         List<ActivityResponse> content = page.getContent().stream()
diff --git a/backend/src/main/java/com/rongyichuang/auth/util/JwtUtil.java b/backend/src/main/java/com/rongyichuang/auth/util/JwtUtil.java
index 489d0f8..6207646 100644
--- a/backend/src/main/java/com/rongyichuang/auth/util/JwtUtil.java
+++ b/backend/src/main/java/com/rongyichuang/auth/util/JwtUtil.java
@@ -8,6 +8,9 @@
 import org.springframework.stereotype.Component;
 
 import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
 import java.util.Date;
 
 /**
@@ -38,7 +41,7 @@
         Date now = new Date();
         Date expiryDate = new Date(now.getTime() + jwtExpiration);
 
-        SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes());
+        SecretKey key = getSigningKey();
 
         JwtBuilder builder = Jwts.builder()
                 .setSubject(userId.toString())
@@ -56,6 +59,29 @@
         }
 
         return builder.signWith(key, SignatureAlgorithm.HS256).compact();
+    }
+
+    /**
+     * 鏍规嵁閰嶇疆鐨勫瘑閽ョ敓鎴愭弧瓒� HMAC-SHA 瑕佹眰鐨勭鍚嶅瘑閽ワ細
+     * - 鑻ユ槑鏂囧瘑閽ラ暱搴︿笉瓒� 256 bit锛屼娇鐢� SHA-256 琛嶇敓涓� 256-bit
+     * - 淇濇寔瀵圭幇鏈� app.jwt.secret 鐨勫吋瀹癸紝涓嶄慨鏀归厤缃敭鍚嶆垨鍏跺畠閫昏緫
+     */
+    private SecretKey getSigningKey() {
+        try {
+            byte[] keyBytes = jwtSecret.getBytes(StandardCharsets.UTF_8);
+            if (keyBytes.length < 32) {
+                MessageDigest digest = MessageDigest.getInstance("SHA-256");
+                keyBytes = digest.digest(keyBytes);
+            }
+            if (keyBytes.length < 32) {
+                byte[] padded = new byte[32];
+                System.arraycopy(keyBytes, 0, padded, 0, Math.min(keyBytes.length, 32));
+                keyBytes = padded;
+            }
+            return new SecretKeySpec(keyBytes, "HmacSHA256");
+        } catch (Exception e) {
+            throw new RuntimeException("鍒濆鍖朖WT绛惧悕瀵嗛挜澶辫触", e);
+        }
     }
 
     /**
@@ -111,7 +137,7 @@
      * 浠巘oken涓В鏋怌laims
      */
     private Claims getClaimsFromToken(String token) {
-        SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes());
+        SecretKey key = getSigningKey();
         return Jwts.parserBuilder()
                 .setSigningKey(key)
                 .build()
diff --git a/backend/src/main/java/com/rongyichuang/config/GraphQLConfig.java b/backend/src/main/java/com/rongyichuang/config/GraphQLConfig.java
index 3eccde8..42eb94f 100644
--- a/backend/src/main/java/com/rongyichuang/config/GraphQLConfig.java
+++ b/backend/src/main/java/com/rongyichuang/config/GraphQLConfig.java
@@ -12,7 +12,6 @@
     @Bean
     public RuntimeWiringConfigurer runtimeWiringConfigurer() {
         return wiringBuilder -> wiringBuilder
-                .scalar(ExtendedScalars.GraphQLLong)
                 .scalar(longScalar());
     }
 
diff --git a/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java b/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
index 8101d60..0accc67 100644
--- a/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
+++ b/backend/src/main/java/com/rongyichuang/config/SecurityConfig.java
@@ -49,6 +49,7 @@
             .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
             .authorizeHttpRequests(auth -> auth
                 .requestMatchers("/auth/**", "/actuator/**", "/test/**", "/cleanup/**").permitAll()
+                .requestMatchers("/api/health/**").permitAll() // 鍏佽鍋ュ悍妫�鏌ョ鐐硅闂�
                 .requestMatchers("/upload/**").permitAll()
                 .requestMatchers("/graphiql/**", "/graphql/**", "/api/graphql/**", "/api/graphiql/**").permitAll() // 鍏佽GraphQL鍜孏raphiQL璁块棶
                 .requestMatchers("/**/graphql", "/**/graphiql").permitAll() // 鏇村娉涚殑GraphQL璺緞鍖归厤
diff --git a/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java b/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
index a58e054..67f728c 100644
--- a/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
+++ b/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
@@ -15,6 +15,7 @@
 import com.rongyichuang.player.dto.response.PlayerRegistrationResponse;
 import com.rongyichuang.player.dto.response.StageJudgeRatingDetailResponse;
 import com.rongyichuang.player.dto.PromotionCompetitionResponse;
+import com.rongyichuang.player.dto.response.PromotionCompetitionPageResponse;
 import com.rongyichuang.player.dto.CompetitionParticipantResponse;
 import com.rongyichuang.player.dto.PromotionInput;
 import com.rongyichuang.player.dto.PromotionResult;
@@ -285,7 +286,7 @@
      * 鑾峰彇姣旇禌鏅嬬骇鍒楄〃
      */
     @QueryMapping
-    public List<PromotionCompetitionResponse> promotionCompetitions(
+    public PromotionCompetitionPageResponse promotionCompetitions(
             @Argument String name,
             @Argument Integer page,
             @Argument Integer size) {
diff --git a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java
index 1ab5cf0..df96807 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerDetailService.java
@@ -158,9 +158,11 @@
         }
         
         Object birthdayObj = row.get("birthday");
-        playerInfo.setBirthday(birthdayObj != null ? 
+        playerInfo.setBirthday(birthdayObj != null ?
             (birthdayObj instanceof java.sql.Date ? ((java.sql.Date) birthdayObj).toString() : birthdayObj.toString()) : null);
-        playerInfo.setEducation(row.get("education") != null ? row.get("education").toString() : "");
+        Object educationObj = row.get("education");
+        log.info("璋冭瘯锛氫粠鏁版嵁搴撴煡璇㈠埌鐨別ducation鍊�: {}", educationObj);
+        playerInfo.setEducation(educationObj != null ? educationObj.toString() : "");
         playerInfo.setIntroduction(row.get("introduction") != null ? row.get("introduction").toString() : "");
 
         // 鏋勫缓鍖哄煙淇℃伅
diff --git a/backend/src/main/java/com/rongyichuang/player/service/PromotionService.java b/backend/src/main/java/com/rongyichuang/player/service/PromotionService.java
index 4ca32ea..e85c772 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/PromotionService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/PromotionService.java
@@ -8,6 +8,7 @@
 import com.rongyichuang.common.repository.MediaRepository;
 import com.rongyichuang.message.service.MessageService;
 import com.rongyichuang.player.dto.*;
+import com.rongyichuang.player.dto.response.PromotionCompetitionPageResponse;
 import com.rongyichuang.player.entity.ActivityPlayer;
 import com.rongyichuang.player.repository.ActivityPlayerRepository;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -45,11 +46,11 @@
     /**
      * 鑾峰彇姣旇禌鏅嬬骇鍒楄〃
      */
-    public List<PromotionCompetitionResponse> getPromotionCompetitions(String name, Integer page, Integer size) {
-        List<PromotionCompetitionResponse> result = new ArrayList<>();
+    public PromotionCompetitionPageResponse getPromotionCompetitions(String name, Integer page, Integer size) {
+        List<PromotionCompetitionResponse> allResults = new ArrayList<>();
         
         // 鏌ヨ鎵�鏈夋湁鏁堢殑涓绘瘮璧涳紙pid = 0锛�
-        List<Activity> competitions = activityRepository.findByPidAndStateOrderByCreateTimeAsc(0L, 1);
+        List<Activity> competitions = activityRepository.findByPidAndStateOrderByCreateTimeDesc(0L, 1);
         
         // 濡傛灉鏈夊悕绉拌繃婊ゆ潯浠讹紝杩涜杩囨护
         if (name != null && !name.trim().isEmpty()) {
@@ -60,7 +61,7 @@
         
         // 涓烘瘡涓瘮璧涙煡璇㈠叾闃舵
         for (Activity competition : competitions) {
-            List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(competition.getId(), 1);
+            List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeDesc(competition.getId(), 1);
             
             for (Activity stage : stages) {
                 // 缁熻褰撳墠闃舵鐨勫弬璧涗汉鏁�
@@ -68,22 +69,27 @@
                 Integer currentCount = playerCountLong != null ? playerCountLong.intValue() : 0;
                 
                 PromotionCompetitionResponse response = new PromotionCompetitionResponse(competition, stage, currentCount);
-                result.add(response);
+                allResults.add(response);
             }
         }
         
-        // 绠�鍗曞垎椤靛鐞嗭紙瀹為檯椤圭洰涓缓璁娇鐢ㄦ暟鎹簱鍒嗛〉锛�
+        // 璁$畻鎬绘暟
+        long totalElements = allResults.size();
+        
+        // 鍒嗛〉澶勭悊
+        List<PromotionCompetitionResponse> pagedResults = allResults;
         if (page != null && size != null && page > 0 && size > 0) {
             int start = (page - 1) * size;
-            int end = Math.min(start + size, result.size());
-            if (start < result.size()) {
-                result = result.subList(start, end);
+            int end = Math.min(start + size, allResults.size());
+            if (start < allResults.size()) {
+                pagedResults = allResults.subList(start, end);
             } else {
-                result = new ArrayList<>();
+                pagedResults = new ArrayList<>();
             }
         }
         
-        return result;
+        // 杩斿洖鍒嗛〉鍝嶅簲瀵硅薄
+        return new PromotionCompetitionPageResponse(pagedResults, totalElements, page, size);
     }
     
     /**
diff --git a/backend/src/main/resources/graphql/player.graphqls b/backend/src/main/resources/graphql/player.graphqls
index ee07e45..373e6fe 100644
--- a/backend/src/main/resources/graphql/player.graphqls
+++ b/backend/src/main/resources/graphql/player.graphqls
@@ -24,7 +24,7 @@
     # 寰俊绔幏鍙栭�夋墜鎶ュ悕鐘舵��
     getPlayerRegistrationState(activityId: ID!): PlayerRegistrationResponse
     # 鑾峰彇姣旇禌鏅嬬骇鍒楄〃
-    promotionCompetitions(name: String, page: Int, size: Int): [PromotionCompetitionResponse!]!
+    promotionCompetitions(name: String, page: Int, size: Int): PromotionCompetitionPageResponse!
     # 鑾峰彇鍙檵绾у弬璧涜�呭垪琛�
     promotableParticipants(currentStageId: ID!): PromotableParticipantsResponse
 }
@@ -310,6 +310,15 @@
     state: Int
 }
 
+# 姣旇禌鏅嬬骇鍒楄〃鍒嗛〉鍝嶅簲绫诲瀷
+type PromotionCompetitionPageResponse {
+    content: [PromotionCompetitionResponse!]!
+    totalElements: Long!
+    page: Int!
+    size: Int!
+    totalPages: Int!
+}
+
 # 鍙檵绾у弬璧涜�呭垪琛ㄥ搷搴旂被鍨�
 type PromotableParticipantsResponse {
     participants: [PromotableParticipantResponse!]!
diff --git a/web/src/api/media.js b/web/src/api/media.js
index c351e39..91f522b 100644
--- a/web/src/api/media.js
+++ b/web/src/api/media.js
@@ -1,5 +1,6 @@
 // 濯掍綋鏌ヨ API
 import { graphqlRequest, API_CONFIG } from '../config/api.ts';
+import { serverUrl } from '../utils/appConfig.js';
 
 const GRAPHQL_ENDPOINT = API_CONFIG.GRAPHQL_ENDPOINT;
 
@@ -80,7 +81,7 @@
   
   for (let attempt = 1; attempt <= maxRetries; attempt++) {
     try {
-      const response = await fetch('http://localhost:8080/api/upload/image', {
+      const response = await fetch(`${serverUrl}/api/upload/image`, {
         method: 'POST',
         headers: headers,
         body: formData,
diff --git a/web/src/api/promotion.js b/web/src/api/promotion.js
index c2b315d..ff02737 100644
--- a/web/src/api/promotion.js
+++ b/web/src/api/promotion.js
@@ -4,17 +4,23 @@
 const GET_PROMOTION_COMPETITIONS = `
   query GetPromotionCompetitions($name: String, $page: Int, $size: Int) {
     promotionCompetitions(name: $name, page: $page, size: $size) {
-      id
-      competitionId
-      competitionName
-      stageName
-      maxParticipants
-      currentCount
-      status
-      startTime
-      endTime
-      sortOrder
-      state
+      content {
+        id
+        competitionId
+        competitionName
+        stageName
+        maxParticipants
+        currentCount
+        status
+        startTime
+        endTime
+        sortOrder
+        state
+      }
+      totalElements
+      page
+      size
+      totalPages
     }
   }
 `
diff --git a/web/src/utils/appConfig.js b/web/src/utils/appConfig.js
index 5b47beb..72b8a12 100644
--- a/web/src/utils/appConfig.js
+++ b/web/src/utils/appConfig.js
@@ -1,25 +1,54 @@
-import { graphqlRequest } from '../config/api.ts';

-

-const GET_APP_CONFIG = `

-  query AppConfig {

-    appConfig {

-      mediaBaseUrl

-    }

-  }

-`;

-

-export async function loadAppConfig() {

-  try {

-    const result = await graphqlRequest(GET_APP_CONFIG);

-    const mediaBaseUrl = result.data?.appConfig?.mediaBaseUrl || '';

-    // 浣滀负鍏ㄥ眬鍙橀噺鏆撮湶

-    window.__APP_MEDIA_BASE_URL__ = mediaBaseUrl;

-    return mediaBaseUrl;

-  } catch (e) {

-    // 濡傛灉GraphQL鏌ヨ澶辫触锛屼娇鐢ㄩ粯璁ら厤缃�

-    console.warn('loadAppConfig failed, using default config:', e?.message || e);

-    const defaultMediaBaseUrl = 'http://localhost:8080';

-    window.__APP_MEDIA_BASE_URL__ = defaultMediaBaseUrl;

-    return defaultMediaBaseUrl;

-  }

+import { graphqlRequest } from '../config/api.ts';
+
+export const serverUrl =
+  (typeof window !== 'undefined' && window.__APP_SERVER_URL__) ||
+  (typeof import.meta !== 'undefined' && import.meta.env && import.meta.env.VITE_SERVER_URL) ||
+  'http://139.155.104.10:8080';
+
+
+
+const GET_APP_CONFIG = `
+
+  query AppConfig {
+
+    appConfig {
+
+      mediaBaseUrl
+
+    }
+
+  }
+
+`;
+
+
+
+export async function loadAppConfig() {
+
+  try {
+
+    const result = await graphqlRequest(GET_APP_CONFIG);
+
+    const mediaBaseUrl = result.data?.appConfig?.mediaBaseUrl || '';
+
+    // 浣滀负鍏ㄥ眬鍙橀噺鏆撮湶
+
+    window.__APP_MEDIA_BASE_URL__ = mediaBaseUrl;
+
+    return mediaBaseUrl;
+
+  } catch (e) {
+
+    // 濡傛灉GraphQL鏌ヨ澶辫触锛屼娇鐢ㄩ粯璁ら厤缃�
+
+    console.warn('loadAppConfig failed, using default config:', e?.message || e);
+
+    const defaultMediaBaseUrl = serverUrl;
+
+    window.__APP_MEDIA_BASE_URL__ = defaultMediaBaseUrl;
+
+    return defaultMediaBaseUrl;
+
+  }
+
 }
\ No newline at end of file
diff --git a/web/src/utils/cos-simple.ts b/web/src/utils/cos-simple.ts
index 6f3e835..76fc4a5 100644
--- a/web/src/utils/cos-simple.ts
+++ b/web/src/utils/cos-simple.ts
@@ -1,65 +1,129 @@
-import axios from 'axios'

-

-// GraphQL鏌ヨ鑾峰彇涓婁紶鍑瘉

-const GET_UPLOAD_CREDENTIALS = `

-  query GetUploadCredentials {

-    getUploadCredentials {

-      bucket

-      region

-      key

-      presignedUrl

-      expiration

-    }

-  }

-`

-

-// 浠庡悗绔疓raphQL鑾峰彇涓婁紶鍑瘉

-const getUploadCredentials = async () => {

-  try {

-    const response = await axios.post('http://localhost:8080/graphql', {

-      query: GET_UPLOAD_CREDENTIALS

-    })

-    

-    if (response.data.errors) {

-      throw new Error(response.data.errors[0].message)

-    }

-    

-    return response.data.data.getUploadCredentials

-  } catch (error) {

-    console.error('鑾峰彇涓婁紶鍑瘉澶辫触:', error)

-    throw error

-  }

-}

-

-/**

- * 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢鍒拌吘璁簯COS

- * @param file 瑕佷笂浼犵殑鏂囦欢

- * @returns Promise<string> 杩斿洖鏂囦欢鐨勮闂甎RL

- */

-export const uploadToCOS = async (file: File): Promise<string> => {

-  try {

-    // 鑾峰彇涓婁紶鍑瘉

-    const credentials = await getUploadCredentials()

-    

-    // 浣跨敤棰勭鍚峌RL

-    const uploadUrl = credentials.presignedUrl

-    

-    // 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢

-    const uploadResponse = await axios.put(uploadUrl, file, {

-      headers: {

-        'Content-Type': file.type,

-      }

-    })

-    

-    if (uploadResponse.status === 200) {

-      // 杩斿洖鏂囦欢鐨勮闂甎RL锛堝幓鎺夋煡璇㈠弬鏁帮級

-      const fileUrl = `https://${credentials.bucket}.cos.${credentials.region}.myqcloud.com/${credentials.key}`

-      return fileUrl

-    } else {

-      throw new Error(`涓婁紶澶辫触锛岀姸鎬佺爜: ${uploadResponse.status}`)

-    }

-    

-  } catch (error) {

-    throw error

-  }

+import axios from 'axios'
+import { serverUrl } from './appConfig.js'
+
+
+
+// GraphQL鏌ヨ鑾峰彇涓婁紶鍑瘉
+
+const GET_UPLOAD_CREDENTIALS = `
+
+  query GetUploadCredentials {
+
+    getUploadCredentials {
+
+      bucket
+
+      region
+
+      key
+
+      presignedUrl
+
+      expiration
+
+    }
+
+  }
+
+`
+
+
+
+// 浠庡悗绔疓raphQL鑾峰彇涓婁紶鍑瘉
+
+const getUploadCredentials = async () => {
+
+  try {
+
+    const response = await axios.post(`${serverUrl}/graphql`, {
+
+      query: GET_UPLOAD_CREDENTIALS
+
+    })
+
+  
+    if (response.data.errors) {
+
+      throw new Error(response.data.errors[0].message)
+
+    }
+
+    
+
+    return response.data.data.getUploadCredentials
+
+  } catch (error) {
+
+    console.error('鑾峰彇涓婁紶鍑瘉澶辫触:', error)
+
+    throw error
+
+  }
+
+}
+
+
+
+/**
+
+ * 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢鍒拌吘璁簯COS
+
+ * @param file 瑕佷笂浼犵殑鏂囦欢
+
+ * @returns Promise<string> 杩斿洖鏂囦欢鐨勮闂甎RL
+
+ */
+
+export const uploadToCOS = async (file: File): Promise<string> => {
+
+  try {
+
+    // 鑾峰彇涓婁紶鍑瘉
+
+    const credentials = await getUploadCredentials()
+
+    
+
+    // 浣跨敤棰勭鍚峌RL
+
+    const uploadUrl = credentials.presignedUrl
+
+    
+
+    // 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢
+
+    const uploadResponse = await axios.put(uploadUrl, file, {
+
+      headers: {
+
+        'Content-Type': file.type,
+
+      }
+
+    })
+
+    
+
+    if (uploadResponse.status === 200) {
+
+      // 杩斿洖鏂囦欢鐨勮闂甎RL锛堝幓鎺夋煡璇㈠弬鏁帮級
+
+      const fileUrl = `https://${credentials.bucket}.cos.${credentials.region}.myqcloud.com/${credentials.key}`
+
+      return fileUrl
+
+    } else {
+
+      throw new Error(`涓婁紶澶辫触锛岀姸鎬佺爜: ${uploadResponse.status}`)
+
+    }
+
+    
+
+  } catch (error) {
+
+    throw error
+
+  }
+
 }
\ No newline at end of file
diff --git a/web/src/utils/cos.ts b/web/src/utils/cos.ts
index 2a61394..53ccd09 100644
--- a/web/src/utils/cos.ts
+++ b/web/src/utils/cos.ts
@@ -1,162 +1,324 @@
-import COS from 'cos-js-sdk-v5'

-import axios from 'axios'

-

-// 浠庡悗绔幏鍙栦复鏃跺瘑閽�

-const getCredentialsFromBackend = async () => {

-  try {

-    const response = await axios.get('http://localhost:8080/api/cos/credentials')

-    return response.data

-  } catch (error) {

-    throw error

-  }

-}

-

-// 鍒涘缓COS瀹炰緥

-const cos = new COS({

-  getAuthorization: async function (options: any, callback: any) {

-    try {

-      console.log('姝e湪浠庡悗绔幏鍙朇OS涓存椂瀵嗛挜...')

-      // 浠庡悗绔幏鍙栦复鏃跺瘑閽�

-      const credentials = await getCredentialsFromBackend()

-      

-      console.log('鎴愬姛鑾峰彇涓存椂瀵嗛挜:', {

-        TmpSecretId: credentials.TmpSecretId?.substring(0, 10) + '...',

-        bucket: credentials.config?.bucket,

-        region: credentials.config?.region

-      })

-      

-      callback({

-        TmpSecretId: credentials.TmpSecretId,

-        TmpSecretKey: credentials.TmpSecretKey,

-        SecurityToken: credentials.SecurityToken,

-        StartTime: credentials.StartTime,

-        ExpiredTime: credentials.ExpiredTime,

-      })

-    } catch (error) {

-      console.error('鑾峰彇涓存椂瀵嗛挜澶辫触:', error)

-      callback(error)

-    }

-  }

-})

-

-// 鑾峰彇COS閰嶇疆淇℃伅

-export const getCOSConfig = async () => {

-  try {

-    const response = await axios.get('http://localhost:8080/api/cos/config')

-    return response.data

-  } catch (error) {

-    console.error('鑾峰彇COS閰嶇疆澶辫触:', error)

-    throw error

-  }

-}

-

-/**

- * 涓婁紶鏂囦欢鍒拌吘璁簯COS

- * @param file 瑕佷笂浼犵殑鏂囦欢

- * @param folder 瀛樺偍鏂囦欢澶硅矾寰勶紝濡� 'avatars/', 'documents/'

- * @returns Promise<string> 杩斿洖鏂囦欢鐨勮闂甎RL

- */

-export const uploadToCOS = async (file: File, folder: string = ''): Promise<string> => {

-  try {

-    // 鑾峰彇COS閰嶇疆

-    const config = await getCOSConfig()

-    

-    // 鐢熸垚鍞竴鏂囦欢鍚�

-    const timestamp = Date.now()

-    const randomStr = Math.random().toString(36).substring(2, 8)

-    const fileExt = file.name.split('.').pop()

-    const fileName = `${folder}${timestamp}_${randomStr}.${fileExt}`

-    

-    console.log('寮�濮嬩笂浼犳枃浠�:', fileName, '鍒板瓨鍌ㄦ《:', config.bucket)

-    

-    return new Promise((resolve, reject) => {

-      cos.uploadFile({

-        Bucket: config.bucket,

-        Region: config.region,

-        Key: fileName,

-        Body: file,

-        SliceSize: 1024 * 1024 * 5, // 澶т簬5MB鐨勬枃浠朵娇鐢ㄥ垎鍧椾笂浼�

-        onProgress: (progressData) => {

-          console.log('涓婁紶杩涘害:', Math.round(progressData.percent * 100) + '%')

-        }

-      }, (err, data) => {

-        if (err) {

-          console.error('涓婁紶澶辫触:', err)

-          reject(err)

-        } else {

-          console.log('涓婁紶鎴愬姛:', data)

-          // 杩斿洖鏂囦欢鐨勮闂甎RL

-          const fileUrl = `https://${data.Location}`

-          resolve(fileUrl)

-        }

-      })

-    })

-  } catch (error) {

-    console.error('涓婁紶鏂囦欢澶辫触:', error)

-    throw error

-  }

-}

-

-/**

- * 鍒犻櫎COS涓殑鏂囦欢

- * @param key 鏂囦欢鐨凨ey锛堣矾寰勶級

- * @returns Promise<boolean>

- */

-export const deleteFromCOS = async (key: string): Promise<boolean> => {

-  try {

-    const config = await getCOSConfig()

-    

-    return new Promise((resolve, reject) => {

-      cos.deleteObject({

-        Bucket: config.bucket,

-        Region: config.region,

-        Key: key

-      }, (err, data) => {

-        if (err) {

-          console.error('鍒犻櫎澶辫触:', err)

-          reject(err)

-        } else {

-          console.log('鍒犻櫎鎴愬姛:', data)

-          resolve(true)

-        }

-      })

-    })

-  } catch (error) {

-    console.error('鍒犻櫎鏂囦欢澶辫触:', error)

-    throw error

-  }

-}

-

-/**

- * 鑾峰彇鏂囦欢鐨勪复鏃惰闂甎RL锛堢敤浜庣鏈夎鍙栫殑鏂囦欢锛�

- * @param key 鏂囦欢鐨凨ey锛堣矾寰勶級

- * @param expires 杩囨湡鏃堕棿锛堢锛夛紝榛樿1灏忔椂

- * @returns Promise<string>

- */

-export const getObjectUrl = async (key: string, expires: number = 3600): Promise<string> => {

-  try {

-    const config = await getCOSConfig()

-    

-    return new Promise((resolve, reject) => {

-      cos.getObjectUrl({

-        Bucket: config.bucket,

-        Region: config.region,

-        Key: key,

-        Expires: expires,

-        Sign: true

-      }, (err, data) => {

-        if (err) {

-          console.error('鑾峰彇URL澶辫触:', err)

-          reject(err)

-        } else {

-          resolve(data.Url)

-        }

-      })

-    })

-  } catch (error) {

-    console.error('鑾峰彇鏂囦欢URL澶辫触:', error)

-    throw error

-  }

-}

-

+import COS from 'cos-js-sdk-v5'
+
+import axios from 'axios'
+import { serverUrl } from './appConfig.js'
+
+
+
+// 浠庡悗绔幏鍙栦复鏃跺瘑閽�
+
+const getCredentialsFromBackend = async () => {
+
+  try {
+
+    const response = await axios.get(`${serverUrl}/api/cos/credentials`)
+
+    return response.data
+
+  } catch (error) {
+
+    throw error
+
+  }
+
+}
+
+
+
+// 鍒涘缓COS瀹炰緥
+
+const cos = new COS({
+
+  getAuthorization: async function (options: any, callback: any) {
+
+    try {
+
+      console.log('姝e湪浠庡悗绔幏鍙朇OS涓存椂瀵嗛挜...')
+
+      // 浠庡悗绔幏鍙栦复鏃跺瘑閽�
+
+      const credentials = await getCredentialsFromBackend()
+
+      
+
+      console.log('鎴愬姛鑾峰彇涓存椂瀵嗛挜:', {
+
+        TmpSecretId: credentials.TmpSecretId?.substring(0, 10) + '...',
+
+        bucket: credentials.config?.bucket,
+
+        region: credentials.config?.region
+
+      })
+
+      
+
+      callback({
+
+        TmpSecretId: credentials.TmpSecretId,
+
+        TmpSecretKey: credentials.TmpSecretKey,
+
+        SecurityToken: credentials.SecurityToken,
+
+        StartTime: credentials.StartTime,
+
+        ExpiredTime: credentials.ExpiredTime,
+
+      })
+
+    } catch (error) {
+
+      console.error('鑾峰彇涓存椂瀵嗛挜澶辫触:', error)
+
+      callback(error)
+
+    }
+
+  }
+
+})
+
+
+
+// 鑾峰彇COS閰嶇疆淇℃伅
+
+export const getCOSConfig = async () => {
+
+  try {
+
+    const response = await axios.get(`${serverUrl}/api/cos/config`)
+
+    return response.data
+
+  } catch (error) {
+
+    console.error('鑾峰彇COS閰嶇疆澶辫触:', error)
+
+    throw error
+
+  }
+
+}
+
+
+
+/**
+
+ * 涓婁紶鏂囦欢鍒拌吘璁簯COS
+
+ * @param file 瑕佷笂浼犵殑鏂囦欢
+
+ * @param folder 瀛樺偍鏂囦欢澶硅矾寰勶紝濡� 'avatars/', 'documents/'
+
+ * @returns Promise<string> 杩斿洖鏂囦欢鐨勮闂甎RL
+
+ */
+
+export const uploadToCOS = async (file: File, folder: string = ''): Promise<string> => {
+
+  try {
+
+    // 鑾峰彇COS閰嶇疆
+
+    const config = await getCOSConfig()
+
+    
+
+    // 鐢熸垚鍞竴鏂囦欢鍚�
+
+    const timestamp = Date.now()
+
+    const randomStr = Math.random().toString(36).substring(2, 8)
+
+    const fileExt = file.name.split('.').pop()
+
+    const fileName = `${folder}${timestamp}_${randomStr}.${fileExt}`
+
+    
+
+    console.log('寮�濮嬩笂浼犳枃浠�:', fileName, '鍒板瓨鍌ㄦ《:', config.bucket)
+
+    
+
+    return new Promise((resolve, reject) => {
+
+      cos.uploadFile({
+
+        Bucket: config.bucket,
+
+        Region: config.region,
+
+        Key: fileName,
+
+        Body: file,
+
+        SliceSize: 1024 * 1024 * 5, // 澶т簬5MB鐨勬枃浠朵娇鐢ㄥ垎鍧椾笂浼�
+
+        onProgress: (progressData) => {
+
+          console.log('涓婁紶杩涘害:', Math.round(progressData.percent * 100) + '%')
+
+        }
+
+      }, (err, data) => {
+
+        if (err) {
+
+          console.error('涓婁紶澶辫触:', err)
+
+          reject(err)
+
+        } else {
+
+          console.log('涓婁紶鎴愬姛:', data)
+
+          // 杩斿洖鏂囦欢鐨勮闂甎RL
+
+          const fileUrl = `https://${data.Location}`
+
+          resolve(fileUrl)
+
+        }
+
+      })
+
+    })
+
+  } catch (error) {
+
+    console.error('涓婁紶鏂囦欢澶辫触:', error)
+
+    throw error
+
+  }
+
+}
+
+
+
+/**
+
+ * 鍒犻櫎COS涓殑鏂囦欢
+
+ * @param key 鏂囦欢鐨凨ey锛堣矾寰勶級
+
+ * @returns Promise<boolean>
+
+ */
+
+export const deleteFromCOS = async (key: string): Promise<boolean> => {
+
+  try {
+
+    const config = await getCOSConfig()
+
+    
+
+    return new Promise((resolve, reject) => {
+
+      cos.deleteObject({
+
+        Bucket: config.bucket,
+
+        Region: config.region,
+
+        Key: key
+
+      }, (err, data) => {
+
+        if (err) {
+
+          console.error('鍒犻櫎澶辫触:', err)
+
+          reject(err)
+
+        } else {
+
+          console.log('鍒犻櫎鎴愬姛:', data)
+
+          resolve(true)
+
+        }
+
+      })
+
+    })
+
+  } catch (error) {
+
+    console.error('鍒犻櫎鏂囦欢澶辫触:', error)
+
+    throw error
+
+  }
+
+}
+
+
+
+/**
+
+ * 鑾峰彇鏂囦欢鐨勪复鏃惰闂甎RL锛堢敤浜庣鏈夎鍙栫殑鏂囦欢锛�
+
+ * @param key 鏂囦欢鐨凨ey锛堣矾寰勶級
+
+ * @param expires 杩囨湡鏃堕棿锛堢锛夛紝榛樿1灏忔椂
+
+ * @returns Promise<string>
+
+ */
+
+export const getObjectUrl = async (key: string, expires: number = 3600): Promise<string> => {
+
+  try {
+
+    const config = await getCOSConfig()
+
+    
+
+    return new Promise((resolve, reject) => {
+
+      cos.getObjectUrl({
+
+        Bucket: config.bucket,
+
+        Region: config.region,
+
+        Key: key,
+
+        Expires: expires,
+
+        Sign: true
+
+      }, (err, data) => {
+
+        if (err) {
+
+          console.error('鑾峰彇URL澶辫触:', err)
+
+          reject(err)
+
+        } else {
+
+          resolve(data.Url)
+
+        }
+
+      })
+
+    })
+
+  } catch (error) {
+
+    console.error('鑾峰彇鏂囦欢URL澶辫触:', error)
+
+    throw error
+
+  }
+
+}
+
+
+
 export default cos
\ No newline at end of file
diff --git a/web/src/views/check-detail.vue b/web/src/views/check-detail.vue
index 57d9776..cfa0d6e 100644
--- a/web/src/views/check-detail.vue
+++ b/web/src/views/check-detail.vue
@@ -417,7 +417,7 @@
 }
 
 // 鑾峰彇瀛﹀巻鏂囨湰
-const getEducationText = (education: number) => {
+const getEducationText = (education: number | string) => {
   const educationMap: Record<number, string> = {
     1: '楂樹腑',
     2: '澶т笓',
@@ -425,7 +425,8 @@
     4: '纭曞+',
     5: '鍗氬+'
   }
-  return educationMap[education] || '-'
+  const numEducation = typeof education === 'string' ? parseInt(education) : education
+  return educationMap[numEducation] || '-'
 }
 
 // 鑾峰彇鐘舵�佹枃鏈�
diff --git a/web/src/views/next-list.vue b/web/src/views/next-list.vue
index a8200db..4158bf8 100644
--- a/web/src/views/next-list.vue
+++ b/web/src/views/next-list.vue
@@ -276,8 +276,15 @@
       size: pagination.size
     })
     
-    competitions.value = data || []
-    pagination.total = data ? data.length : 0
+    // 澶勭悊鍒嗛〉鍝嶅簲瀵硅薄
+    if (data && data.content) {
+      competitions.value = data.content
+      pagination.total = parseInt(data.totalElements) || 0
+    } else {
+      // 鍏煎鏃х殑杩斿洖鏍煎紡锛堝鏋滃悗绔繕娌℃洿鏂帮級
+      competitions.value = data || []
+      pagination.total = data ? data.length : 0
+    }
   } catch (error) {
     console.error('鑾峰彇姣旇禌鏅嬬骇鍒楄〃澶辫触:', error)
     ElMessage.error('鑾峰彇姣旇禌鏁版嵁澶辫触: ' + (error.message || '鏈煡閿欒'))
diff --git a/web/vite.config.ts b/web/vite.config.ts
index 95ede8f..d0a27bd 100644
--- a/web/vite.config.ts
+++ b/web/vite.config.ts
@@ -10,12 +10,25 @@
     }
   },
   server: {
+    host: '0.0.0.0',
     port: 3000,
     open: true,
     proxy: {
       '/api': {
-        target: 'http://localhost:8080',
+        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')
       }
diff --git a/wx/app.js b/wx/app.js
index 12709af..ba9f405 100644
--- a/wx/app.js
+++ b/wx/app.js
@@ -4,7 +4,10 @@
     userInfo: null,
     token: null,
     sessionKey: null, // 寰俊浼氳瘽瀵嗛挜锛岀敤浜庤В瀵嗘墜鏈哄彿绛夋晱鎰熸暟鎹�
-    baseUrl: 'http://localhost:8080/api/graphql', // 鍚庡彴GraphQL鎺ュ彛鍦板潃
+    baseUrl: 'https://ryc.9village.cn/api/graphql', // 鍚庡彴GraphQL鎺ュ彛鍦板潃
+    loginUrl:'https://ryc.9village.cn',
+    // baseUrl: 'http://localhost:8080/api/graphql', // 鍚庡彴GraphQL鎺ュ彛鍦板潃
+    // loginUrl:'http://localhost:8080',
     hasPhoneAuth: false, // 鏄惁宸叉巿鏉冩墜鏈哄彿
     rejectPhone: false, // 鏄惁鎷掔粷杩囨墜鏈哄彿鎺堟潈
     cos: {
@@ -101,10 +104,6 @@
     wx.login({
       success: (res) => {
         if (res.code) {
-          console.log('鉁� 鑾峰彇寰俊鐧诲綍code鎴愬姛')
-          console.log('鐧诲綍code:', res.code)
-          console.log('code闀垮害:', res.code.length)
-          console.log('鍑嗗璋冪敤鍚庣wxLogin鎺ュ彛...')
           this.wxLogin(res.code)
         } else {
           console.error('鉂� 鑾峰彇寰俊鐧诲綍code澶辫触')
@@ -130,29 +129,21 @@
     const deviceInfo = this.getDeviceInfo()
     const requestData = {
       code: code,
-      loginIp: '127.0.0.1', // 灏忕▼搴忔棤娉曡幏鍙栫湡瀹濱P锛屼娇鐢ㄩ粯璁ゅ��
+      // loginIp: '127.0.0.1', // 灏忕▼搴忔棤娉曡幏鍙栫湡瀹濱P锛屼娇鐢ㄩ粯璁ゅ��
       deviceInfo: deviceInfo
     }
     
-    console.log('=== 鍑嗗璋冪敤鍚庣wxLogin鎺ュ彛 ===')
-    console.log('璇锋眰URL:', 'http://localhost:8080/api/auth/wx-login')
-    console.log('璁惧淇℃伅:', deviceInfo)
-    console.log('璇锋眰鍙傛暟:', requestData)
-    console.log('璇锋眰寮�濮嬫椂闂�:', new Date().toISOString())
+
     
     wx.request({
-      url: 'http://localhost:8080/api/auth/wx-login',
+      url: this.globalData.loginUrl + '/api/auth/wx-login',
       method: 'POST',
       header: {
         'Content-Type': 'application/json'
       },
       data: requestData,
       success: (res) => {
-        console.log('=== 鍚庣wxLogin鎺ュ彛鍝嶅簲 ===')
-        console.log('鍝嶅簲鏃堕棿:', new Date().toISOString())
-        console.log('HTTP鐘舵�佺爜:', res.statusCode)
-        console.log('鍝嶅簲澶�:', res.header)
-        console.log('鍝嶅簲鏁版嵁:', JSON.stringify(res.data, null, 2))
+
         
         if (res.statusCode !== 200) {
           console.error('鉂� HTTP璇锋眰澶辫触锛岀姸鎬佺爜:', res.statusCode)
diff --git a/wx/app.json b/wx/app.json
index 8bea719..6e950e6 100644
--- a/wx/app.json
+++ b/wx/app.json
@@ -17,7 +17,7 @@
   "window": {
     "backgroundTextStyle": "light",
     "navigationBarBackgroundColor": "#ffffff",
-    "navigationBarTitleText": "钃夋槗鍒�",
+    "navigationBarTitleText": "钃塭鍒�",
     "navigationBarTextStyle": "black",
     "backgroundColor": "#f5f5f5"
   },
diff --git a/wx/pages/index/index.js b/wx/pages/index/index.js
index cf405ea..f9cba3f 100644
--- a/wx/pages/index/index.js
+++ b/wx/pages/index/index.js
@@ -21,19 +21,20 @@
     // 绛涢�夋潯浠�
     filterStatus: 'all', // all, upcoming, ongoing, ended
     // 杞挱鍥惧綋鍓嶇储寮�
-    currentBannerIndex: 0
+    currentBannerIndex: 0,
+    // 鍒嗕韩鐩稿叧鏁版嵁
+    shareActivityId: null,
+    shareActivityName: null
   },
 
   onLoad(options) {
     console.log('棣栭〉鍔犺浇')
-    this.loadBanners()
-    this.loadActivities()
   },
 
   onShow() {
     console.log('棣栭〉鏄剧ず')
     // 缁熶竴绯荤粺瀵艰埅鏍忔爣棰�
-    try { wx.setNavigationBarTitle({ title: '钃夋槗鍒�' }) } catch (e) {}
+    try { wx.setNavigationBarTitle({ title: '钃塭鍒�' }) } catch (e) {}
     // 妫�鏌ョ櫥褰曠姸鎬�
     if (!app.globalData.token) {
       app.login()
@@ -42,6 +43,9 @@
     if (typeof this.getTabBar === 'function' && this.getTabBar()) {
       this.getTabBar().init();
     }
+    // 鍔犺浇鏁版嵁
+    this.loadBanners()
+    this.loadActivities()
   },
 
   onPullDownRefresh() {
@@ -175,9 +179,9 @@
     if (filterStatus !== 'all') {
       // 鏍规嵁filterStatus鏄犲皠鍒板搴旂殑state鍊�
       const stateMapping = {
-        'upcoming': 1,    // 鍗冲皢寮�濮�
-        'ongoing': 2,     // 杩涜涓�  
-        'ended': 3        // 宸茬粨鏉�
+        'upcoming': 1,    // 鍗冲皢寮�濮� -> 鍙戝竷鐘舵��
+        'ongoing': 1,     // 杩涜涓� -> 鍙戝竷鐘舵��  
+        'ended': 2        // 宸茬粨鏉� -> 鍏抽棴鐘舵��
       }
       stateFilter = stateMapping[filterStatus]
     }
@@ -214,7 +218,7 @@
       page: currentPage,
       size: pageSize,
       name: nameFilter,
-      state: stateFilter
+      state: 1
     }).then(data => {
       if (data.activities) {
         let newActivities = data.activities.content
@@ -434,5 +438,164 @@
     return now <= signupDeadline && 
            activity.state === 'SIGNUP' &&
            activity.playerCount < activity.playerMax
+  },
+
+  // 鍒嗕韩鍗曚釜姣旇禌
+  onShareActivity(e) {
+    const { id, name } = e.currentTarget.dataset
+    
+    // 鏄剧ず鍒嗕韩閫夐」
+    wx.showActionSheet({
+      itemList: ['鍒嗕韩缁欐湅鍙�', '鐢熸垚鍒嗕韩娴锋姤'],
+      success: (res) => {
+        if (res.tapIndex === 0) {
+          // 鍒嗕韩缁欐湅鍙�
+          this.shareToFriend(id, name)
+        } else if (res.tapIndex === 1) {
+          // 鐢熸垚鍒嗕韩娴锋姤
+          this.generateSharePoster(id, name)
+        }
+      },
+      fail: (res) => {
+        console.log('鐢ㄦ埛鍙栨秷鍒嗕韩')
+      }
+    })
+  },
+
+  // 鍒嗕韩缁欐湅鍙�
+  shareToFriend(activityId, activityName) {
+    wx.showShareMenu({
+      withShareTicket: true,
+      menus: ['shareAppMessage', 'shareTimeline']
+    })
+    
+    // 璁剧疆褰撳墠瑕佸垎浜殑娲诲姩淇℃伅
+    this.setData({
+      shareActivityId: activityId,
+      shareActivityName: activityName
+    })
+    
+    // 瑙﹀彂鍒嗕韩
+    wx.updateShareMenu({
+      withShareTicket: true,
+      isUpdatableMessage: true,
+      activityId: 'share_activity_' + activityId,
+      templateInfo: {
+        parameterList: [{
+          name: 'activity_name',
+          value: activityName
+        }]
+      }
+    })
+    
+    wx.showToast({
+      title: '璇风偣鍑诲彸涓婅鍒嗕韩',
+      icon: 'none',
+      duration: 2000
+    })
+  },
+
+  // 鐢熸垚鍒嗕韩娴锋姤
+  generateSharePoster(activityId, activityName) {
+    wx.showLoading({
+      title: '鐢熸垚娴锋姤涓�...'
+    })
+    
+    // 杩欓噷鍙互璋冪敤鍚庣API鐢熸垚鍒嗕韩娴锋姤
+    // 鎴栬�呬娇鐢╟anvas鍦ㄥ墠绔敓鎴�
+    setTimeout(() => {
+      wx.hideLoading()
+      wx.showToast({
+        title: '娴锋姤鐢熸垚鍔熻兘寮�鍙戜腑',
+        icon: 'none',
+        duration: 2000
+      })
+    }, 1500)
+  },
+
+  // 椤甸潰鍒嗕韩鍔熻兘 - 鍒嗕韩缁欐湅鍙�
+  onShareAppMessage(res) {
+    console.log('鍒嗕韩缁欐湅鍙�', res)
+    
+    // 濡傛灉鏄粠姣旇禌鍗$墖鍒嗕韩
+    if (this.data.shareActivityId && this.data.shareActivityName) {
+      const shareData = {
+        title: `${this.data.shareActivityName} - 钃塭鍒涙瘮璧涘钩鍙癭,
+        path: `/pages/activity/detail?id=${this.data.shareActivityId}`,
+        imageUrl: '', // 鍙互璁剧疆鍒嗕韩鍥剧墖
+        success: (res) => {
+          console.log('鍒嗕韩鎴愬姛', res)
+          wx.showToast({
+            title: '鍒嗕韩鎴愬姛',
+            icon: 'success',
+            duration: 2000
+          })
+          // 娓呴櫎鍒嗕韩鐘舵��
+          this.setData({
+            shareActivityId: null,
+            shareActivityName: null
+          })
+        },
+        fail: (res) => {
+          console.log('鍒嗕韩澶辫触', res)
+          wx.showToast({
+            title: '鍒嗕韩澶辫触',
+            icon: 'none',
+            duration: 2000
+          })
+        }
+      }
+      return shareData
+    }
+    
+    // 榛樿鍒嗕韩鏁翠釜棣栭〉
+    return {
+      title: '钃塭鍒涙瘮璧涘钩鍙� - 鍙戠幇绮惧僵姣旇禌',
+      path: '/pages/index/index',
+      imageUrl: '', // 鍙互璁剧疆榛樿鍒嗕韩鍥剧墖
+      success: (res) => {
+        console.log('鍒嗕韩鎴愬姛', res)
+        wx.showToast({
+          title: '鍒嗕韩鎴愬姛',
+          icon: 'success',
+          duration: 2000
+        })
+      },
+      fail: (res) => {
+        console.log('鍒嗕韩澶辫触', res)
+        wx.showToast({
+          title: '鍒嗕韩澶辫触',
+          icon: 'none',
+          duration: 2000
+        })
+      }
+    }
+  },
+
+  // 鍒嗕韩鍒版湅鍙嬪湀
+  onShareTimeline() {
+    console.log('鍒嗕韩鍒版湅鍙嬪湀')
+    
+    return {
+      title: '钃塭鍒涙瘮璧涘钩鍙� - 鍙戠幇绮惧僵姣旇禌',
+      query: '',
+      imageUrl: '', // 鍙互璁剧疆鍒嗕韩鍥剧墖
+      success: (res) => {
+        console.log('鍒嗕韩鍒版湅鍙嬪湀鎴愬姛', res)
+        wx.showToast({
+          title: '鍒嗕韩鎴愬姛',
+          icon: 'success',
+          duration: 2000
+        })
+      },
+      fail: (res) => {
+        console.log('鍒嗕韩鍒版湅鍙嬪湀澶辫触', res)
+        wx.showToast({
+          title: '鍒嗕韩澶辫触',
+          icon: 'none',
+          duration: 2000
+        })
+      }
+    }
   }
 })
\ No newline at end of file
diff --git a/wx/pages/index/index.json b/wx/pages/index/index.json
index b92d296..305ce4b 100644
--- a/wx/pages/index/index.json
+++ b/wx/pages/index/index.json
@@ -1,3 +1,3 @@
 {
-  "navigationBarTitleText": "钃夋槗鍒�"
+  "navigationBarTitleText": "钃塭鍒�"
 }
\ No newline at end of file
diff --git a/wx/pages/index/index.wxml b/wx/pages/index/index.wxml
index eb5a980..dd5a8d3 100644
--- a/wx/pages/index/index.wxml
+++ b/wx/pages/index/index.wxml
@@ -1,6 +1,17 @@
 <wxs src="./filters.wxs" module="filters" />
 <!--pages/index/index.wxml-->
 <view class="container">
+  <!-- 椤甸潰澶撮儴鍒嗕韩鍖哄煙 -->
+  <view class="header-section">
+    <view class="page-title">钃塭鍒涙瘮璧涘钩鍙�</view>
+    <view class="share-section">
+      <!-- <button class="share-btn" open-type="share">
+        <text class="share-icon">馃摛</text>
+        <text class="share-text">鍒嗕韩</text>
+      </button> -->
+    </view>
+  </view>
+
   <!-- 鎼滅储鏍� - 鏆傛椂闅愯棌 -->
   <view class="search-bar" style="display: none;">
     <view class="search-input-wrapper">
@@ -93,6 +104,9 @@
           <view class="registered">宸叉姤鍚嶏細{{item.playerCount}}浜�</view>
           <view class="btn-row">
             <button class="ghost-btn" catchtap="onActivityDetailTap" data-idx="{{index}}"  data-id="{{item.id}}" data-xid="{{item.name}}"  >鏌ョ湅璇︽儏</button>
+            <!-- <button class="share-activity-btn" catchtap="onShareActivity" data-idx="{{index}}" data-id="{{item.id}}" data-name="{{item.name}}">
+              <text class="share-icon-small">馃摛</text>
+            </button> -->
           </view>
         </view>
       </view>
diff --git a/wx/pages/index/index.wxss b/wx/pages/index/index.wxss
index 71aa4c6..d2ef0c1 100644
--- a/wx/pages/index/index.wxss
+++ b/wx/pages/index/index.wxss
@@ -1,5 +1,83 @@
 /* pages/index/index.wxss */
 
+/* 椤甸潰澶撮儴鍒嗕韩鍖哄煙鏍峰紡 */
+.header-section {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 20rpx;
+  background: #ffffff;
+  border-bottom: 1rpx solid #f0f0f0;
+}
+
+.page-title {
+  font-size: 36rpx;
+  font-weight: 700;
+  color: #0f172a;
+}
+
+.share-section {
+  display: flex;
+  align-items: center;
+}
+
+.share-btn {
+  display: flex;
+  align-items: center;
+  gap: 8rpx;
+  background: #007aff;
+  color: #ffffff;
+  border: none;
+  border-radius: 50rpx;
+  padding: 16rpx 24rpx;
+  font-size: 26rpx;
+  line-height: 1;
+}
+
+.share-btn::after {
+  border: none;
+}
+
+.share-btn:active {
+  background: #0056cc;
+}
+
+.share-icon {
+  font-size: 28rpx;
+}
+
+.share-text {
+  font-size: 26rpx;
+}
+
+/* 姣旇禌鍗$墖鍒嗕韩鎸夐挳鏍峰紡 */
+.share-activity-btn {
+  width: 80rpx;
+  height: 60rpx;
+  background: #f8fafc;
+  border: 1rpx solid #e2e8f0;
+  border-radius: 12rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-left: 16rpx;
+  padding: 0;
+  line-height: 1;
+}
+
+.share-activity-btn::after {
+  border: none;
+}
+
+.share-activity-btn:active {
+  background: #e2e8f0;
+}
+
+.share-icon-small {
+  font-size: 24rpx;
+  color: #64748b;
+}
+
 /* 鎼滅储鏍忔牱寮� */
 .search-bar {
   padding: 20rpx;
diff --git a/wx/pages/judge/review.js b/wx/pages/judge/review.js
index c8d16d2..112e080 100644
--- a/wx/pages/judge/review.js
+++ b/wx/pages/judge/review.js
@@ -149,9 +149,27 @@
           videos: detail.submissionFiles ? detail.submissionFiles
             .filter(file => file.mediaType === 2)
             .map(file => file.fullUrl || file.url) : [],
+          mediaList: (detail.submissionFiles || []).map(file => {
+            const mt = file.mediaType
+            let typeStr = ''
+            if (mt === 1 || mt === 'image' || (typeof mt === 'string' && mt.startsWith('image'))) typeStr = 'image'
+            else if (mt === 2 || mt === 'video' || (typeof mt === 'string' && mt.startsWith('video'))) typeStr = 'video'
+            else if ((file.fileExt || '').toLowerCase().includes('pdf')) typeStr = 'pdf'
+            else if ((file.fileExt || '').toLowerCase().includes('doc')) typeStr = 'word'
+            else typeStr = 'file'
+            return {
+              id: file.id,
+              name: file.name,
+              size: file.fileSize,
+              mediaType: typeStr,
+              thumbUrl: file.fullThumbUrl || file.thumbUrl || file.fullUrl || file.url,
+              url: file.fullUrl || file.url
+            }
+          }),
           participant: {
             id: detail.playerInfo.id,
             name: detail.playerInfo.name,
+            phone: detail.playerInfo.phone || '',
             gender: this.getGenderText(detail.playerInfo.gender),
             birthday: detail.playerInfo.birthday || '',
             region: detail.regionInfo ? detail.regionInfo.name : '',
@@ -293,20 +311,39 @@
     })
   },
 
-  // 濯掍綋鐐瑰嚮
+  // 濯掍綋鐐瑰嚮锛堥�氳繃 index 瀹氫綅 mediaList 椤癸級
   onMediaTap(e) {
-    const { url, type } = e.currentTarget.dataset
-    
-    if (type === 'image') {
+    const { index } = e.currentTarget.dataset
+    const item = this.data.submission?.mediaList?.[index]
+    if (!item) return
+    if (item.mediaType === 'image') {
+      const imgs = (this.data.submission.mediaList || [])
+        .filter(it => it.mediaType === 'image')
+        .map(it => it.url)
       wx.previewImage({
-        current: url,
-        urls: this.data.submission.images || []
+        current: item.url,
+        urls: imgs.length ? imgs : [item.url]
       })
-    } else if (type === 'video') {
+    } else if (item.mediaType === 'video') {
       this.setData({
         showMediaPreview: true,
-        currentMedia: url,
+        currentMedia: item.url,
         mediaType: 'video'
+      })
+    } else {
+      wx.downloadFile({
+        url: item.url,
+        success: (res) => {
+          if (res.statusCode === 200) {
+            wx.openDocument({
+              filePath: res.tempFilePath,
+              showMenu: true
+            })
+          } else {
+            wx.showToast({ title: '棰勮澶辫触', icon: 'none' })
+          }
+        },
+        fail: () => wx.showToast({ title: '涓嬭浇澶辫触', icon: 'none' })
       })
     }
   },
@@ -317,6 +354,44 @@
       showMediaPreview: false,
       currentMedia: null
     })
+  },
+
+  // 鐐瑰嚮棰勮鎸夐挳锛氬浘鐗�/瑙嗛鐢� wx.previewMedia锛屾枃妗g敤 openDocument
+  onPreviewTap(e) {
+    const { index } = e.currentTarget.dataset
+    const list = this.data.submission?.mediaList || []
+    const item = list[index]
+    if (!item) return
+
+    if (item.mediaType === 'image' || item.mediaType === 'video') {
+      const mediaList = list
+        .filter(m => m.mediaType === 'image' || m.mediaType === 'video')
+        .map(m => ({
+          url: m.url,
+          type: m.mediaType === 'video' ? 'video' : 'image',
+          poster: m.thumbUrl || m.url
+        }))
+      const current = Math.max(0, mediaList.findIndex(m => m.url === item.url))
+      wx.previewMedia({
+        sources: mediaList,
+        current
+      })
+    } else {
+      wx.downloadFile({
+        url: item.url,
+        success: (res) => {
+          if (res.statusCode === 200) {
+            wx.openDocument({
+              filePath: res.tempFilePath,
+              showMenu: true
+            })
+          } else {
+            wx.showToast({ title: '棰勮澶辫触', icon: 'none' })
+          }
+        },
+        fail: () => wx.showToast({ title: '涓嬭浇澶辫触', icon: 'none' })
+      })
+    }
   },
 
   // 涓嬭浇鏂囦欢
@@ -560,30 +635,15 @@
   },
   */
 
-  // 鑱旂郴鍙傝禌鑰�
+  // 鑱旂郴鍙傝禌鑰咃細鐩存帴鎷ㄦ墦鐢佃瘽
   onContactParticipant() {
-    const { submission } = this.data
-    
-    if (submission.participant) {
-      wx.showActionSheet({
-        itemList: ['鍙戦�佹秷鎭�', '鏌ョ湅璇︽儏'],
-        success: (res) => {
-          switch (res.tapIndex) {
-            case 0:
-              // 鍙戦�佹秷鎭姛鑳�
-              wx.navigateTo({
-                url: `/pages/chat/chat?userId=${submission.participant.id}`
-              })
-              break
-            case 1:
-              // 鏌ョ湅鐢ㄦ埛璇︽儏
-              wx.navigateTo({
-                url: `/pages/user/profile?userId=${submission.participant.id}`
-              })
-              break
-          }
-        }
-      })
+    const phone =
+      this.data.submission?.participant?.phone ||
+      this.data.submission?.participant?.userInfo?.phone
+    if (phone) {
+      wx.makePhoneCall({ phoneNumber: String(phone) })
+    } else {
+      wx.showToast({ title: '鏃犺仈绯荤數璇�', icon: 'none' })
     }
   },
 
@@ -619,7 +679,7 @@
   // 鍒嗕韩椤甸潰
   onShareAppMessage() {
     return {
-      title: '钃夋槗鍒� - 璇勫浣滃搧',
+      title: '钃塭鍒� - 璇勫浣滃搧',
       path: '/pages/index/index'
     }
   }
diff --git a/wx/pages/judge/review.wxml b/wx/pages/judge/review.wxml
index 0a2cae6..3b6d467 100644
--- a/wx/pages/judge/review.wxml
+++ b/wx/pages/judge/review.wxml
@@ -74,6 +74,9 @@
           <view class="media-info">
             <text class="media-name">{{item.name}}</text>
             <text class="media-size">{{getFileSizeText(item.size)}}</text>
+            <view class="preview-btn" catchtap="onPreviewTap" data-index="{{index}}">
+              <text>棰勮</text>
+            </view>
           </view>
         </view>
       </view>
diff --git a/wx/pages/judge/review.wxss b/wx/pages/judge/review.wxss
index 24f04d5..edc89f9 100644
--- a/wx/pages/judge/review.wxss
+++ b/wx/pages/judge/review.wxss
@@ -454,6 +454,26 @@
   font-size: 24rpx;
 }
 
+/* 棰勮鎸夐挳鏍峰紡锛堜娇鐢� view 鍛堢幇涓烘寜閽瑙傦級 */
+.preview-btn {
+  align-self: flex-start;
+  margin-top: 8rpx;
+  padding: 10rpx 24rpx;
+  border: 2rpx solid #007aff;
+  color: #007aff;
+  border-radius: 999rpx;
+  font-size: 24rpx;
+  line-height: 1;
+  background-color: #ffffff;
+}
+.preview-btn:active {
+  background-color: #e6f0ff;
+}
+
+/* 淇濇寔濯掍綋淇℃伅鍖虹殑绱у噾鎬� */
+.media-info .media-name {
+  margin-bottom: 4rpx;
+}
 @media (max-width: 375px) {
   .container {
     padding-bottom: 100rpx;
diff --git a/wx/pages/message/message.js b/wx/pages/message/message.js
index 4fa1f29..508d1fb 100644
--- a/wx/pages/message/message.js
+++ b/wx/pages/message/message.js
@@ -24,11 +24,6 @@
     // 妫�鏌ョ敤鎴锋槸鍚﹀凡鐧诲綍
     const userInfo = app.globalData.userInfo
     if (!userInfo || !userInfo.userId) {
-      console.error('鐢ㄦ埛鏈櫥褰曟垨userId涓嶅瓨鍦�')
-      wx.showToast({
-        title: '璇峰厛鐧诲綍',
-        icon: 'error'
-      })
       return
     }
 
diff --git a/wx/pages/profile/profile.js b/wx/pages/profile/profile.js
index a1123db..d55cf34 100644
--- a/wx/pages/profile/profile.js
+++ b/wx/pages/profile/profile.js
@@ -690,7 +690,7 @@
   // 鍒嗕韩椤甸潰
   onShareAppMessage() {
     return {
-      title: '钃夋槗鍒� - 鎴戠殑涓汉涓績',
+      title: '钃塭鍒� - 鎴戠殑涓汉涓績',
       path: '/pages/index/index'
     }
   }
diff --git a/wx/pages/registration/registration.js b/wx/pages/registration/registration.js
index 719571c..66dd0d5 100644
--- a/wx/pages/registration/registration.js
+++ b/wx/pages/registration/registration.js
@@ -1292,7 +1292,7 @@
         // 绗笁姝ワ細鎶ュ悕鎴愬姛鍚庡己鍒惰皟鐢╳xlogin鑾峰彇鏂扮殑JWT token
         console.log('馃摫 鎶ュ悕鎴愬姛锛屽紑濮嬪己鍒惰皟鐢╳xlogin鑾峰彇鏂扮殑JWT token')
         try {
-          await app.wxLogin()
+          await app.login()
           console.log('鉁� 鎶ュ悕鎴愬姛鍚巜xlogin璋冪敤鎴愬姛锛屽凡鑾峰彇鏂扮殑JWT token')
         } catch (wxLoginError) {
           console.error('鉂� 鎶ュ悕鎴愬姛鍚巜xlogin璋冪敤澶辫触:', wxLoginError)

--
Gitblit v1.8.0