| | |
| | | import com.rongyichuang.judge.service.JudgeService; |
| | | import com.rongyichuang.player.entity.Player; |
| | | import com.rongyichuang.player.service.PlayerService; |
| | | import com.rongyichuang.player.repository.ActivityPlayerRepository; |
| | | import com.rongyichuang.common.repository.MediaRepository; |
| | | import com.rongyichuang.common.entity.Media; |
| | | import com.rongyichuang.common.enums.MediaTargetType; |
| | | import com.rongyichuang.user.dto.response.UserProfile; |
| | | import com.rongyichuang.user.dto.response.UserStats; |
| | | import com.rongyichuang.user.dto.response.UserRegistration; |
| | | import com.rongyichuang.user.dto.response.UserProject; |
| | | import com.rongyichuang.user.dto.request.UserInput; |
| | | import com.rongyichuang.user.dto.response.UserProfileInfo; |
| | | import com.rongyichuang.user.entity.User; |
| | | import com.rongyichuang.user.repository.UserRepository; |
| | | import com.rongyichuang.player.dto.response.SubmissionMediaResponse; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.graphql.data.method.annotation.Argument; |
| | | import org.springframework.graphql.data.method.annotation.QueryMapping; |
| | | import org.springframework.graphql.data.method.annotation.MutationMapping; |
| | | import org.springframework.stereotype.Controller; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import jakarta.persistence.EntityManager; |
| | | import jakarta.persistence.PersistenceContext; |
| | | import jakarta.persistence.Query; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | |
| | | /** |
| | | * 用户GraphQL解析器 |
| | |
| | | private PlayerService playerService; |
| | | |
| | | @Autowired |
| | | private ActivityPlayerRepository activityPlayerRepository; |
| | | |
| | | @Autowired |
| | | private MediaRepository mediaRepository; |
| | | |
| | | @Autowired |
| | | private UserRepository userRepository; |
| | | |
| | | @PersistenceContext |
| | | private EntityManager entityManager; |
| | | |
| | | @Autowired |
| | | private UserContextUtil userContextUtil; |
| | | |
| | | @Value("${app.media-url}") |
| | | private String mediaBaseUrl; |
| | | |
| | | /** |
| | | * 获取当前用户档案 |
| | |
| | | |
| | | UserProfile profile = new UserProfile(); |
| | | profile.setId(userId.toString()); |
| | | |
| | | // 获取用户基本信息 |
| | | Optional<User> userOpt = userRepository.findById(userId); |
| | | User user = null; |
| | | if (userOpt.isPresent()) { |
| | | user = userOpt.get(); |
| | | // 设置性别 |
| | | if (user.getGender() != null) { |
| | | profile.setGender(user.getGender() == 1 ? "MALE" : "FEMALE"); |
| | | } |
| | | // 设置生日 |
| | | if (user.getBirthday() != null) { |
| | | profile.setBirthday(user.getBirthday().toString()); |
| | | } |
| | | } |
| | | |
| | | List<String> roles = new ArrayList<>(); |
| | | |
| | |
| | | } |
| | | |
| | | profile.setRoles(roles); |
| | | logger.debug("userProfile构建完成,roles={}, name={}, phone={}", roles, profile.getName(), profile.getPhone()); |
| | | |
| | | // 获取用户头像 |
| | | try { |
| | | List<Media> avatarMediaList = mediaRepository.findByTargetTypeAndTargetIdAndState( |
| | | MediaTargetType.USER_AVATAR.getValue(), |
| | | userId, |
| | | 1 |
| | | ); |
| | | if (!avatarMediaList.isEmpty()) { |
| | | Media avatarMedia = avatarMediaList.get(0); |
| | | String fullAvatarUrl = buildFullMediaUrl(avatarMedia.getPath()); |
| | | profile.setAvatar(fullAvatarUrl); |
| | | logger.debug("找到用户头像: {} -> {}", avatarMedia.getPath(), fullAvatarUrl); |
| | | } else { |
| | | logger.debug("用户{}没有找到头像", userId); |
| | | } |
| | | } catch (Exception e) { |
| | | logger.warn("获取用户头像失败: {}", e.getMessage()); |
| | | } |
| | | |
| | | logger.debug("userProfile构建完成,roles={}, name={}, phone={}, avatar={}", |
| | | roles, profile.getName(), profile.getPhone(), profile.getAvatar()); |
| | | profile.setCreatedAt(java.time.LocalDateTime.now().toString()); |
| | | |
| | | return profile; |
| | |
| | | @QueryMapping |
| | | public UserStats userStats() { |
| | | try { |
| | | Long userId = UserContextUtil.getCurrentUserId(); |
| | | Long userId = userContextUtil.getCurrentUserId(); |
| | | if (userId == null) { |
| | | return null; |
| | | } |
| | |
| | | @QueryMapping |
| | | public List<UserRegistration> myRegistrations(@Argument Integer limit) { |
| | | try { |
| | | Long userId = UserContextUtil.getCurrentUserId(); |
| | | Long userId = userContextUtil.getCurrentUserId(); |
| | | if (userId == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | // 这里应该实现真正的报名记录查询逻辑 |
| | | // 暂时返回空列表 |
| | | return new ArrayList<>(); |
| | | // 构建SQL查询,获取用户的报名记录 |
| | | StringBuilder sql = new StringBuilder(); |
| | | sql.append("SELECT ap.id, a.id as activity_id, a.name as activity_name, "); |
| | | sql.append("ap.project_name, ap.state, ap.create_time, "); |
| | | sql.append("m.path as cover_image_path "); |
| | | sql.append("FROM t_activity_player ap "); |
| | | sql.append("INNER JOIN t_player p ON ap.player_id = p.id "); |
| | | sql.append("INNER JOIN t_activity a ON ap.activity_id = a.id "); |
| | | sql.append("LEFT JOIN t_media m ON a.cover_image_id = m.id "); |
| | | sql.append("WHERE p.user_id = :userId "); |
| | | sql.append("ORDER BY ap.create_time DESC"); |
| | | |
| | | if (limit != null && limit > 0) { |
| | | sql.append(" LIMIT :limit"); |
| | | } |
| | | |
| | | Query query = entityManager.createNativeQuery(sql.toString()); |
| | | query.setParameter("userId", userId); |
| | | if (limit != null && limit > 0) { |
| | | query.setParameter("limit", limit); |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | List<Object[]> results = query.getResultList(); |
| | | |
| | | List<UserRegistration> registrations = new ArrayList<>(); |
| | | for (Object[] row : results) { |
| | | UserRegistration registration = new UserRegistration(); |
| | | registration.setId(String.valueOf(row[0])); |
| | | |
| | | // 创建ActivityInfo |
| | | UserRegistration.ActivityInfo activityInfo = new UserRegistration.ActivityInfo(); |
| | | activityInfo.setId(String.valueOf(row[1])); |
| | | activityInfo.setTitle((String) row[2]); |
| | | |
| | | // 设置封面图片 |
| | | if (row[6] != null) { |
| | | UserRegistration.MediaInfo mediaInfo = new UserRegistration.MediaInfo(); |
| | | mediaInfo.setPath((String) row[6]); |
| | | mediaInfo.setFullUrl(buildFullMediaUrl((String) row[6])); |
| | | activityInfo.setCoverImage(mediaInfo); |
| | | } |
| | | |
| | | registration.setActivity(activityInfo); |
| | | |
| | | // 设置状态 |
| | | Integer state = (Integer) row[4]; |
| | | String status = "pending"; |
| | | if (state != null) { |
| | | switch (state) { |
| | | case 0: status = "pending"; break; |
| | | case 1: status = "approved"; break; |
| | | case 2: status = "rejected"; break; |
| | | default: status = "unknown"; break; |
| | | } |
| | | } |
| | | registration.setStatus(status); |
| | | |
| | | // 设置报名时间 |
| | | if (row[5] != null) { |
| | | registration.setRegistrationTime(row[5].toString()); |
| | | } |
| | | |
| | | registrations.add(registration); |
| | | } |
| | | |
| | | return registrations; |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | logger.error("获取用户报名记录失败", e); |
| | | return new ArrayList<>(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取我的项目列表 |
| | | */ |
| | | @QueryMapping |
| | | public List<UserProject> myProjects() { |
| | | try { |
| | | Long userId = userContextUtil.getCurrentUserId(); |
| | | logger.debug("获取用户项目列表,userId: {}", userId); |
| | | if (userId == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | // 查询用户的所有项目(state = 0, 1, 2) |
| | | // 使用 t_activity_player join t_player join t_user 的方式通过用户ID查询 |
| | | String sql = """ |
| | | SELECT ap.id, ap.project_name, a.name as activity_name, ap.state, ap.create_time |
| | | FROM t_activity_player ap |
| | | JOIN t_player p ON ap.player_id = p.id |
| | | JOIN t_activity a ON a.id = ap.activity_id |
| | | WHERE p.user_id = ? AND ap.state IN (0, 1, 2) |
| | | ORDER BY ap.create_time DESC |
| | | """; |
| | | |
| | | Query query = entityManager.createNativeQuery(sql); |
| | | query.setParameter(1, userId); |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | List<Object[]> results = query.getResultList(); |
| | | |
| | | List<UserProject> projects = new ArrayList<>(); |
| | | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | for (Object[] row : results) { |
| | | String id = row[0].toString(); |
| | | String projectName = row[1] != null ? row[1].toString() : ""; |
| | | String activityName = row[2] != null ? row[2].toString() : ""; |
| | | String status = row[3] != null ? row[3].toString() : "0"; |
| | | String createTime = row[4] != null ? row[4].toString() : ""; |
| | | |
| | | UserProject project = new UserProject(id, projectName, activityName, status, createTime); |
| | | |
| | | // 查询项目的提交文件 |
| | | List<SubmissionMediaResponse> submissionFiles = getSubmissionFiles(Long.parseLong(id)); |
| | | project.setSubmissionFiles(submissionFiles); |
| | | |
| | | projects.add(project); |
| | | } |
| | | |
| | | logger.debug("查询到 {} 个项目", projects.size()); |
| | | return projects; |
| | | } catch (Exception e) { |
| | | logger.error("获取用户项目列表失败", e); |
| | | return new ArrayList<>(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取项目的提交文件 |
| | | * @param activityPlayerId 活动参与者ID |
| | | * @return 提交文件列表 |
| | | */ |
| | | private List<SubmissionMediaResponse> getSubmissionFiles(Long activityPlayerId) { |
| | | try { |
| | | // 查询提交的媒体文件 (target_type = 5 表示活动参与者提交的文件,state = 1 表示有效状态) |
| | | List<Media> medias = mediaRepository.findByTargetTypeAndTargetIdAndState(5, activityPlayerId, 1); |
| | | |
| | | List<SubmissionMediaResponse> submissionFiles = new ArrayList<>(); |
| | | for (Media media : medias) { |
| | | SubmissionMediaResponse response = new SubmissionMediaResponse(); |
| | | response.setId(media.getId()); |
| | | response.setName(media.getName()); |
| | | response.setPath(media.getPath()); |
| | | response.setUrl(buildFullMediaUrl(media.getPath())); |
| | | response.setFullUrl(buildFullMediaUrl(media.getPath())); |
| | | response.setFullThumbUrl(buildFullMediaUrl(media.getThumbPath())); |
| | | response.setFileExt(media.getFileExt()); |
| | | response.setFileSize(media.getFileSize() != null ? media.getFileSize().longValue() : null); |
| | | response.setMediaType(media.getMediaType()); |
| | | response.setThumbUrl(buildFullMediaUrl(media.getThumbPath())); |
| | | submissionFiles.add(response); |
| | | } |
| | | |
| | | return submissionFiles; |
| | | } catch (Exception e) { |
| | | logger.error("获取项目提交文件失败,activityPlayerId: {}", activityPlayerId, e); |
| | | return new ArrayList<>(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 构建完整的媒体URL |
| | | * @param path 媒体路径 |
| | | * @return 完整的URL |
| | | */ |
| | | private String buildFullMediaUrl(String path) { |
| | | if (!StringUtils.hasText(path)) { |
| | | return null; |
| | | } |
| | | |
| | | // 如果路径已经是完整URL,直接返回 |
| | | if (path.startsWith("http://") || path.startsWith("https://")) { |
| | | return path; |
| | | } |
| | | |
| | | // 构建完整URL |
| | | if (StringUtils.hasText(mediaBaseUrl)) { |
| | | // 确保baseUrl以/结尾,path不以/开头 |
| | | String baseUrl = mediaBaseUrl.endsWith("/") ? mediaBaseUrl : mediaBaseUrl + "/"; |
| | | String relativePath = path.startsWith("/") ? path.substring(1) : path; |
| | | return baseUrl + relativePath; |
| | | } |
| | | |
| | | // 如果没有配置baseUrl,返回原路径 |
| | | return path; |
| | | } |
| | | |
| | | /** |
| | | * 保存用户信息 |
| | | */ |
| | | @MutationMapping |
| | | public UserProfileInfo saveUserInfo(@Argument UserInput input) { |
| | | try { |
| | | Long userId = userContextUtil.getCurrentUserId(); |
| | | logger.debug("进入saveUserInfo,解析到用户ID: {}", userId); |
| | | if (userId == null) { |
| | | throw new RuntimeException("用户未登录"); |
| | | } |
| | | |
| | | // 查找现有用户 |
| | | Optional<User> userOpt = userRepository.findById(userId); |
| | | User user; |
| | | |
| | | if (userOpt.isPresent()) { |
| | | // 用户存在,更新信息 |
| | | user = userOpt.get(); |
| | | logger.debug("更新现有用户信息,用户ID: {}", userId); |
| | | } else { |
| | | // 用户不存在,创建新用户 |
| | | user = new User(); |
| | | user.setId(userId); |
| | | logger.debug("创建新用户,用户ID: {}", userId); |
| | | } |
| | | |
| | | // 更新用户基本信息(不包含头像) |
| | | user.setName(input.getName()); |
| | | user.setPhone(input.getPhone()); |
| | | |
| | | // 处理性别转换:MALE -> 1, FEMALE -> 0 |
| | | if ("MALE".equals(input.getGender())) { |
| | | user.setGender(1); |
| | | } else if ("FEMALE".equals(input.getGender())) { |
| | | user.setGender(0); |
| | | } |
| | | |
| | | // 处理生日转换 |
| | | if (StringUtils.hasText(input.getBirthday())) { |
| | | try { |
| | | LocalDate birthday = LocalDate.parse(input.getBirthday()); |
| | | user.setBirthday(birthday); |
| | | } catch (Exception e) { |
| | | logger.warn("生日格式解析失败: {}", input.getBirthday(), e); |
| | | } |
| | | } |
| | | |
| | | // 保存用户基本信息 |
| | | user = userRepository.save(user); |
| | | logger.debug("用户信息保存成功,用户ID: {}", user.getId()); |
| | | |
| | | // 构建返回结果 |
| | | UserProfileInfo result = new UserProfileInfo(); |
| | | result.setId(user.getId().toString()); |
| | | result.setName(user.getName()); |
| | | result.setPhone(user.getPhone()); |
| | | // 正确处理性别转换:1 -> MALE, 0 -> FEMALE, null -> null |
| | | if (user.getGender() != null) { |
| | | result.setGender(user.getGender() == 1 ? "MALE" : "FEMALE"); |
| | | } else { |
| | | result.setGender(null); |
| | | } |
| | | result.setBirthday(user.getBirthday() != null ? user.getBirthday().toString() : null); |
| | | result.setWxOpenId(user.getWxOpenid()); |
| | | result.setUnionId(user.getWxUnionid()); |
| | | |
| | | // 查找并设置头像URL(从t_media表获取) |
| | | try { |
| | | List<Media> avatarMedias = mediaRepository.findByTargetTypeAndTargetIdAndState( |
| | | MediaTargetType.USER_AVATAR.getValue(), |
| | | userId, |
| | | 1 |
| | | ); |
| | | if (!avatarMedias.isEmpty()) { |
| | | Media avatarMedia = avatarMedias.get(0); |
| | | result.setAvatar(buildFullMediaUrl(avatarMedia.getPath())); |
| | | } |
| | | } catch (Exception e) { |
| | | logger.warn("获取头像失败: {}", e.getMessage(), e); |
| | | } |
| | | |
| | | return result; |
| | | } catch (Exception e) { |
| | | logger.error("保存用户信息失败", e); |
| | | throw new RuntimeException("保存用户信息失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | } |