Codex Assistant
6 小时以前 c8dffd157cd8b62023b26e62a0b92c152d959423
build(backend): switch to thin-jar layout (split libs into target/lib); chore: remove test-* files; misc updates
10个文件已修改
49个文件已删除
6982 ■■■■■ 已修改文件
backend/pom.xml 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/rongyichuang/auth/controller/AuthController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/rongyichuang/player/dto/CompetitionParticipantResponse.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantResponse.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/rongyichuang/player/entity/Player.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerService.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/java/com/rongyichuang/user/service/UserService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/src/main/resources/application.yml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/test-saveUserInfo-updated.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-activity-detail.js 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-activity-id-debug.js 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-activity-time-debug.js 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-actual-user-data.js 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-all-gender-fixes.js 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-filter-fix.js 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-gender-birthday-fix.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-gender-fix.js 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-graphql-queries-only.js 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-graphql-time-data.js 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-health-check.js 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-index-queries.js 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-jwt-debug.js 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-jwt-parsing.js 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-jwt-wxopenid-fixed.js 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-miniprogram-activity-detail.js 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-miniprogram-data-flow.js 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-miniprogram-format.js 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-miniprogram-graphql.js 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-miniprogram-login-status.js 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-miniprogram-review-fix.js 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-navigation-params.js 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-pagination-fix.js 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-phone-auth-required.js 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-phone-display-fixed.js 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-player-phone-fix.js 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-playercount-fix.js 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-registration-debug.html 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-registration-logic.js 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-review-fix-validation.js 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-review-fix-with-token.js 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-review-page-queries.js 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-review-queries.js 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-review-submit-fix.js 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-saveUserInfo-debug.js 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-saveUserInfo-fixed.js 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-saveUserInfo-no-phone.js 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-saveUserInfo-simple-no-avatar.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-saveUserInfo-simple.js 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-saveUserInfo-with-token.js 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-saveUserInfo-with-valid-token.js 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-user-judge-debug.js 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-with-existing-token.js 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-wx-activity-data.js 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-wx-login-rest.js 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-wx-login-with-code.js 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-wx-login.js 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test-wxs-format.js 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wx/pages/project/detail.js 246 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wx/pages/registration/registration.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
backend/pom.xml
@@ -150,9 +150,52 @@
    <build>
        <plugins>
            <!-- 禁用 Spring Boot 胖包重打包,改为瘦包 + 外置依赖 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <!-- 在打包阶段复制所有依赖到 target/lib -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.6.1</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <includeScope>runtime</includeScope>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </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>
        </plugins>
    </build>
backend/src/main/java/com/rongyichuang/auth/controller/AuthController.java
@@ -34,7 +34,7 @@
     * Web端用户登录
     */
    @PostMapping("/web-login")
    public ResponseEntity<LoginResponse> webLogin(@Valid @RequestBody LoginRequest request) {
    public ResponseEntity<?> webLogin(@Valid @RequestBody LoginRequest request) {
        logger.info("收到Web登录请求,手机号: {}", request.getPhone());
        try {
            LoginResponse response = authService.login(request);
@@ -42,7 +42,25 @@
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            logger.error("Web登录失败,手机号: {}, 错误: {}", request.getPhone(), e.getMessage());
            return ResponseEntity.badRequest().build();
            // 返回包含错误信息的JSON响应
            return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage()));
        }
    }
    // 错误响应类
    public static class ErrorResponse {
        private String message;
        public ErrorResponse(String message) {
            this.message = message;
        }
        public String getMessage() {
            return message;
        }
        public void setMessage(String message) {
            this.message = message;
        }
    }
@@ -51,11 +69,17 @@
     */
    @PostMapping("/wx-login")
    public ResponseEntity<WxLoginResponse> wxLogin(@RequestBody WxLoginRequest request) {
        logger.info("收到微信登录请求,openid: {}", request.getWxOpenid());
        try {
            WxLoginResponse response = authService.wxLogin(request);
            logger.info("微信登录成功,openid: {}", request.getWxOpenid());
            return ResponseEntity.ok(response);
        } catch (JsonProcessingException e) {
            logger.error("微信登录JSON处理异常,openid: {}, 错误: {}", request.getWxOpenid(), e.getMessage());
            return ResponseEntity.badRequest().build();
        } catch (Exception e) {
            logger.error("微信登录失败,openid: {}, 错误: {}", request.getWxOpenid(), e.getMessage(), e);
            return ResponseEntity.status(500).build();
        }
    }
backend/src/main/java/com/rongyichuang/player/dto/CompetitionParticipantResponse.java
@@ -27,9 +27,21 @@
        this.id = activityPlayer.getId();
        this.playerName = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getName() : "";
        this.projectName = activityPlayer.getProjectName();
        // 从User实体获取phone,而不是从Player实体(Player.phone已废弃)
        this.phone = activityPlayer.getPlayer() != null && activityPlayer.getPlayer().getUser() != null ?
            activityPlayer.getPlayer().getUser().getPhone() : "";
        // 临时使用废弃的Player.phone字段,后续需要从服务层传入User的phone
        this.phone = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getPhone() : "";
        this.averageScore = activityPlayer.getTotalScore();
        this.ratingCount = 0; // 需要从评分表中统计
        this.applyTime = activityPlayer.getCreateTime() != null ?
            activityPlayer.getCreateTime().format(FORMATTER) : null;
        this.state = activityPlayer.getState();
    }
    public CompetitionParticipantResponse(ActivityPlayer activityPlayer, String userPhone) {
        this.id = activityPlayer.getId();
        this.playerName = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getName() : "";
        this.projectName = activityPlayer.getProjectName();
        // 从参数获取User的phone
        this.phone = userPhone != null ? userPhone : "";
        this.averageScore = activityPlayer.getTotalScore();
        this.ratingCount = 0; // 需要从评分表中统计
        this.applyTime = activityPlayer.getCreateTime() != null ? 
backend/src/main/java/com/rongyichuang/player/dto/PromotableParticipantResponse.java
@@ -28,9 +28,22 @@
        this.id = activityPlayer.getId();
        this.playerName = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getName() : "";
        this.projectName = activityPlayer.getProjectName();
        // 从User实体获取phone,而不是从Player实体(Player.phone已废弃)
        this.phone = activityPlayer.getPlayer() != null && activityPlayer.getPlayer().getUser() != null ?
            activityPlayer.getPlayer().getUser().getPhone() : "";
        // 临时使用废弃的Player.phone字段,后续需要从服务层传入User的phone
        this.phone = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getPhone() : "";
        this.averageScore = averageScore;
        this.ratingCount = ratingCount != null ? ratingCount : 0;
        this.applyTime = activityPlayer.getCreateTime() != null ?
            activityPlayer.getCreateTime().format(FORMATTER) : null;
        this.state = activityPlayer.getState();
        this.playerId = activityPlayer.getPlayerId();
    }
    public PromotableParticipantResponse(ActivityPlayer activityPlayer, BigDecimal averageScore, Integer ratingCount, String userPhone) {
        this.id = activityPlayer.getId();
        this.playerName = activityPlayer.getPlayer() != null ? activityPlayer.getPlayer().getName() : "";
        this.projectName = activityPlayer.getProjectName();
        // 从参数获取User的phone
        this.phone = userPhone != null ? userPhone : "";
        this.averageScore = averageScore;
        this.ratingCount = ratingCount != null ? ratingCount : 0;
        this.applyTime = activityPlayer.getCreateTime() != null ? 
backend/src/main/java/com/rongyichuang/player/entity/Player.java
@@ -26,7 +26,7 @@
     * @deprecated 此字段已废弃,请使用关联User实体的phone字段
     */
    @Deprecated
    @Column(name = "phone", length = 32, nullable = false, unique = true)
    @Column(name = "phone", length = 32)
    private String phone;
    /**
backend/src/main/java/com/rongyichuang/player/service/ActivityPlayerService.java
@@ -19,6 +19,7 @@
import com.rongyichuang.media.service.MediaV2Service;
import com.rongyichuang.media.dto.MediaSaveInput;
import com.rongyichuang.message.service.MessageService;
import com.rongyichuang.auth.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -61,6 +62,9 @@
    @Autowired
    private MessageService messageService;
    @Autowired
    private JwtUtil jwtUtil;
    public ActivityPlayer getMyActivityPlayer(Long activityId) {
@@ -240,6 +244,18 @@
            String phone = input.getPlayerInfo().getPhone();
            String name = input.getPlayerInfo().getName();
            
            // 获取当前用户的wxopenid(从JWT token中)
            String currentWxOpenid = null;
            try {
                String token = userContextUtil.getTokenFromRequest();
                if (token != null && jwtUtil.validateToken(token)) {
                    currentWxOpenid = jwtUtil.getWxOpenidFromToken(token);
                    log.debug("从JWT token中获取到wxopenid: {}", currentWxOpenid);
                }
            } catch (Exception e) {
                log.warn("获取当前用户wxopenid时发生异常: {}", e.getMessage());
            }
            // 先查找现有用户
            Optional<User> existingUserOpt = userService.findByPhone(phone);
            
@@ -251,6 +267,18 @@
                // 更新用户的生日信息
                if (input.getPlayerInfo().getBirthDate() != null) {
                    user.setBirthday(input.getPlayerInfo().getBirthDate());
                }
                // 更新wxopenid(如果当前token中包含且不为空)
                if (currentWxOpenid != null && !currentWxOpenid.trim().isEmpty()) {
                    // 检查这个openid是否已经被其他用户使用
                    Optional<User> existingUserWithOpenid = userService.findByWxOpenid(currentWxOpenid);
                    if (existingUserWithOpenid.isEmpty() || existingUserWithOpenid.get().getId().equals(user.getId())) {
                        user.setWxOpenid(currentWxOpenid);
                        log.info("更新用户wxopenid: {}", currentWxOpenid);
                    } else {
                        log.warn("wxopenid {} 已被其他用户使用,用户ID: {}", currentWxOpenid, existingUserWithOpenid.get().getId());
                    }
                }
                
                user = userService.save(user);
@@ -270,6 +298,18 @@
                    newUser.setBirthday(input.getPlayerInfo().getBirthDate());
                }
                
                // 设置wxopenid(如果当前token中包含且不为空)
                if (currentWxOpenid != null && !currentWxOpenid.trim().isEmpty()) {
                    // 检查这个openid是否已经被其他用户使用
                    Optional<User> existingUserWithOpenid = userService.findByWxOpenid(currentWxOpenid);
                    if (existingUserWithOpenid.isEmpty()) {
                        newUser.setWxOpenid(currentWxOpenid);
                        log.info("为新用户设置wxopenid: {}", currentWxOpenid);
                    } else {
                        log.warn("wxopenid {} 已被其他用户使用,用户ID: {}", currentWxOpenid, existingUserWithOpenid.get().getId());
                    }
                }
                newUser = userService.save(newUser);
                log.info("为小程序报名成功创建新用户,用户ID: {}", newUser.getId());
                return newUser;
backend/src/main/java/com/rongyichuang/user/service/UserService.java
@@ -126,6 +126,13 @@
    }
    /**
     * 根据微信openid查找用户
     */
    public Optional<User> findByWxOpenid(String wxOpenid) {
        return userRepository.findByWxOpenid(wxOpenid);
    }
    /**
     * 保存用户
     */
    public User save(User user) {
backend/src/main/resources/application.yml
@@ -78,8 +78,11 @@
  level:
    com.rongyichuang: DEBUG
    org.springframework.security: DEBUG
    org.springframework.transaction: DEBUG
    org.springframework.orm.jpa: DEBUG
    org.hibernate.SQL: DEBUG
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
    org.hibernate.engine.transaction: DEBUG
# 应用配置
app:
backend/test-saveUserInfo-updated.js
File was deleted
test-activity-detail.js
File was deleted
test-activity-id-debug.js
File was deleted
test-activity-time-debug.js
File was deleted
test-actual-user-data.js
File was deleted
test-all-gender-fixes.js
File was deleted
test-filter-fix.js
File was deleted
test-gender-birthday-fix.js
File was deleted
test-gender-fix.js
File was deleted
test-graphql-queries-only.js
File was deleted
test-graphql-time-data.js
File was deleted
test-health-check.js
File was deleted
test-index-queries.js
File was deleted
test-jwt-debug.js
File was deleted
test-jwt-parsing.js
File was deleted
test-jwt-wxopenid-fixed.js
File was deleted
test-miniprogram-activity-detail.js
File was deleted
test-miniprogram-data-flow.js
File was deleted
test-miniprogram-format.js
File was deleted
test-miniprogram-graphql.js
File was deleted
test-miniprogram-login-status.js
File was deleted
test-miniprogram-review-fix.js
File was deleted
test-navigation-params.js
File was deleted
test-pagination-fix.js
File was deleted
test-phone-auth-required.js
File was deleted
test-phone-display-fixed.js
File was deleted
test-player-phone-fix.js
File was deleted
test-playercount-fix.js
File was deleted
test-registration-debug.html
File was deleted
test-registration-logic.js
File was deleted
test-review-fix-validation.js
File was deleted
test-review-fix-with-token.js
File was deleted
test-review-page-queries.js
File was deleted
test-review-queries.js
File was deleted
test-review-submit-fix.js
File was deleted
test-saveUserInfo-debug.js
File was deleted
test-saveUserInfo-fixed.js
File was deleted
test-saveUserInfo-no-phone.js
File was deleted
test-saveUserInfo-simple-no-avatar.js
File was deleted
test-saveUserInfo-simple.js
File was deleted
test-saveUserInfo-with-token.js
File was deleted
test-saveUserInfo-with-valid-token.js
File was deleted
test-user-judge-debug.js
File was deleted
test-with-existing-token.js
File was deleted
test-wx-activity-data.js
File was deleted
test-wx-login-rest.js
File was deleted
test-wx-login-with-code.js
File was deleted
test-wx-login.js
File was deleted
test-wxs-format.js
File was deleted
wx/pages/project/detail.js
@@ -10,7 +10,13 @@
    error: '',
    statusText: '',
    genderText: '',
    educationText: ''
    educationText: '',
    // 时间轴相关数据
    timeline: [],
    timelineLoading: false,
    timelineError: '',
    showRatingDetail: false,
    ratingDetail: null
  },
  onLoad(options) {
@@ -66,6 +72,9 @@
          educationText: this.getEducationText(projectDetail.playerInfo?.education),
          loading: false
        })
        // 加载时间轴数据
        this.loadProjectTimeline()
      } else {
        throw new Error('项目详情获取失败')
      }
@@ -80,47 +89,71 @@
  // 从API获取项目详情
  async getProjectDetailFromAPI(projectId) {
    // 构建GraphQL查询
    // 构建GraphQL查询 - 与后端schema匹配
    const query = `
      query GetProjectDetail($id: ID!) {
        activityPlayerDetail(id: $id) {
          id
          activityId
          playerId
          playerName
          playerGender
          playerPhone
          playerEducation
          playerBirthDate
          playerIdCard
          playerAddress
          activityName
          projectName
          projectDescription
          projectCategory
          projectTags
          projectFiles {
            id
            fileName
            fileUrl
            fileSize
            fileType
            uploadTime
          }
          submitTime
          reviewTime
          reviewerId
          reviewerName
          score
          rating {
            id
            judgeId
            judgeName
            score
            feedback
            ratingTime
          }
          state
          description
          feedback
          state
          stageId
          playerInfo {
            id
            name
            phone
            gender
            birthday
            education
            introduction
            description
            avatarUrl
            avatar {
              id
              name
              path
            }
            userInfo {
              userId
              name
              phone
              avatarUrl
              avatar {
                id
                name
                path
              }
            }
          }
          regionInfo {
            id
            name
            fullPath
          }
          submissionFiles {
            id
            name
            path
            url
            fullUrl
            fullThumbUrl
            fileExt
            fileSize
            thumbUrl
          }
          ratingForm {
            schemeId
            schemeName
            totalMaxScore
            items {
              id
              name
              maxScore
              weight
            }
          }
        }
      }
    `
@@ -137,20 +170,30 @@
  async getRatingStatsFromAPI(projectId) {
    const query = `
      query GetRatingStats($activityPlayerId: ID!) {
        ratingStats(activityPlayerId: $activityPlayerId) {
          averageScore
          totalRatings
          scoreDistribution {
            score
            count
          }
        judgeRatingsForPlayer(activityPlayerId: $activityPlayerId) {
          judgeId
          judgeName
          hasRated
          totalScore
          ratingTime
        }
        averageScoreForPlayer(activityPlayerId: $activityPlayerId)
      }
    `
    try {
      const result = await app.graphqlRequest(query, { activityPlayerId: projectId })
      return result.ratingStats
      const ratings = result.judgeRatingsForPlayer || []
      const averageScore = result.averageScoreForPlayer || 0
      // 只计算已评分的评委
      const ratedJudges = ratings.filter(rating => rating.hasRated)
      return {
        averageScore: averageScore,
        totalRatings: ratedJudges.length,
        ratings: ratings
      }
    } catch (error) {
      throw error
    }
@@ -172,6 +215,110 @@
    } catch (error) {
      throw error
    }
  },
  // 加载项目时间轴
  async loadProjectTimeline() {
    try {
      this.setData({
        timelineLoading: true,
        timelineError: ''
      })
      const timeline = await this.getProjectTimelineFromAPI(this.data.projectId)
      if (timeline && timeline.stages) {
        // 处理时间轴数据
        const processedTimeline = timeline.stages.map(stage => {
          return {
            stageId: stage.stageId,
            stageName: stage.stageName,
            matchTime: stage.matchTime,
            matchTimeText: stage.matchTime ? this.formatDateTime(stage.matchTime) : '时间待定',
            participated: stage.participated,
            activityPlayerId: stage.activityPlayerId,
            averageScore: stage.averageScore,
            ratingCount: stage.ratingCount,
            hasRating: stage.hasRating,
            scoreText: stage.averageScore ? `${stage.averageScore.toFixed(1)}分` : '',
            isClickable: stage.hasRating && stage.activityPlayerId
          }
        })
        this.setData({
          timeline: processedTimeline,
          timelineLoading: false
        })
      } else {
        this.setData({
          timeline: [],
          timelineLoading: false
        })
      }
    } catch (error) {
      console.error('加载时间轴失败:', error)
      this.setData({
        timelineError: error.message || '时间轴加载失败',
        timelineLoading: false
      })
    }
  },
  // 从API获取项目时间轴
  async getProjectTimelineFromAPI(activityPlayerId) {
    const query = `
      query GetProjectTimeline($activityPlayerId: ID!) {
        projectStageTimeline(activityPlayerId: $activityPlayerId) {
          activityId
          activityName
          stages {
            stageId
            stageName
            matchTime
            sortOrder
            participated
            activityPlayerId
            averageScore
            ratingCount
            hasRating
            latestRatingTime
          }
        }
      }
    `
    try {
      const result = await app.graphqlRequest(query, { activityPlayerId })
      return result.projectStageTimeline
    } catch (error) {
      throw error
    }
  },
  // 打开阶段详情
  openStageDetail(e) {
    const { playerId, clickable, participated } = e.currentTarget.dataset
    if (!clickable || !participated || !playerId) {
      wx.showToast({
        title: '暂无评分详情',
        icon: 'none'
      })
      return
    }
    // 跳转到评分详情页面或显示详情弹窗
    wx.navigateTo({
      url: `/pages/review/stage-detail?playerId=${playerId}`
    })
  },
  // 关闭阶段详情
  closeStageDetail() {
    this.setData({
      showRatingDetail: false,
      ratingDetail: null
    })
  },
  // 预览文件
@@ -430,6 +577,8 @@
  // 获取性别文本
  getGenderText(gender) {
    const genderMap = {
      1: '男',
      2: '女',
      'MALE': '男',
      'FEMALE': '女'
    }
@@ -443,9 +592,14 @@
      'COLLEGE': '大专',
      'BACHELOR': '本科',
      'MASTER': '硕士',
      'DOCTOR': '博士'
      'DOCTOR': '博士',
      '高中及以下': '高中及以下',
      '大专': '大专',
      '本科': '本科',
      '硕士': '硕士',
      '博士': '博士'
    }
    return educationMap[education] || ''
    return educationMap[education] || education || ''
  },
  // 分享功能
wx/pages/registration/registration.js
@@ -1313,9 +1313,11 @@
      
    } catch (error) {
      console.error('提交失败:', error)
      wx.showToast({
        title: error.message || '提交失败,请重试',
        icon: 'none'
      wx.showModal({
        title: '提交失败',
        content: error.message || '提交失败,请重试',
        showCancel: false,
        confirmText: '我知道了'
      })
    } finally {
      this.setData({ isSubmitting: false })