lrj
1 天以前 dc643ba44fd2a426263015491268a0f0d6b4671d
backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
@@ -2,26 +2,36 @@
import com.rongyichuang.activity.dto.ActivityInput;
import com.rongyichuang.activity.dto.ActivityJudgeInput;
import com.rongyichuang.activity.dto.ActivityJudgeResponse;
import com.rongyichuang.activity.dto.ActivityResponse;
import com.rongyichuang.activity.dto.ActivityStageInput;
import com.rongyichuang.activity.entity.Activity;
import com.rongyichuang.activity.entity.ActivityJudge;
import com.rongyichuang.activity.repository.ActivityJudgeRepository;
import com.rongyichuang.activity.repository.ActivityRepository;
import com.rongyichuang.judge.entity.Judge;
import com.rongyichuang.player.repository.ActivityPlayerRepository;
import com.rongyichuang.judge.repository.JudgeRepository;
import com.rongyichuang.common.dto.PageRequest;
import com.rongyichuang.common.dto.PageResponse;
import com.rongyichuang.rating.entity.RatingScheme;
import com.rongyichuang.rating.repository.RatingSchemeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@@ -35,23 +45,46 @@
    private ActivityJudgeRepository activityJudgeRepository;
    
    @Autowired
    private JudgeRepository judgeRepository;
    @Autowired
    private RatingSchemeRepository ratingSchemeRepository;
    @Autowired
    private ActivityPlayerRepository activityPlayerRepository;
    
    /**
     * 分页查询比赛列表
     */
    public PageResponse<ActivityResponse> findCompetitions(PageRequest pageRequest, String name) {
    public PageResponse<ActivityResponse> findActivities(PageRequest pageRequest, String name) {
        Pageable pageable = pageRequest.toPageable();
        Page<Activity> page;
        if (StringUtils.hasText(name)) {
            page = activityRepository.findByPidAndStateAndNameContainingOrderByCreateTimeDesc(0L, 1, name, pageable);
            page = activityRepository.findByPidAndNameContainingOrderByCreateTimeDesc(0L, name, pageable);
        } else {
            page = activityRepository.findByPidAndStateOrderByCreateTimeDesc(0L, 1, pageable);
            // 查询所有主活动(pid = 0)
            page = activityRepository.findByPidOrderByCreateTimeDesc(0L, pageable);
        }
        List<ActivityResponse> content = page.getContent().stream()
            .map(ActivityResponse::new)
            .map(activity -> {
                ActivityResponse response = new ActivityResponse(activity);
                // 设置参赛人数(只统计第一阶段的审核通过学员人数)
                int playerCount = 0;
                Activity firstStage = activityRepository.findFirstStageByActivityId(activity.getId());
                if (firstStage != null) {
                    // 如果有第一阶段,统计第一阶段的审核通过人数
                    Long playerCountLong = activityPlayerRepository.countByStageIdAndState(firstStage.getId(), 1);
                    playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
                } else {
                    // 如果没有阶段,统计活动本身的审核通过人数
                    Long playerCountLong = activityPlayerRepository.countByActivityIdAndState(activity.getId(), 1);
                    playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
                }
                response.setPlayerCount(playerCount);
                return response;
            })
            .collect(Collectors.toList());
        
        return new PageResponse<>(content, page.getTotalElements(), page.getNumber(), page.getSize());
@@ -66,14 +99,30 @@
            Activity activity = activityOpt.get();
            ActivityResponse response = new ActivityResponse(activity);
            
            // 设置参赛人数(审核通过的报名数量)
            Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
            int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
            response.setPlayerCount(playerCount);
            // 如果是比赛,加载其阶段
            if (activity.isCompetition()) {
            if (activity.isMainActivity()) {
                List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(id, 1);
                List<ActivityResponse> stageResponses = stages.stream()
                    .map(ActivityResponse::new)
                    .map(stage -> {
                        ActivityResponse stageResponse = new ActivityResponse(stage);
                        // 设置阶段的参赛人数
                        Long stagePlayerCountLong = activityPlayerRepository.countByActivityId(stage.getId());
                        int stagePlayerCount = stagePlayerCountLong != null ? stagePlayerCountLong.intValue() : 0;
                        stageResponse.setPlayerCount(stagePlayerCount);
                        return stageResponse;
                    })
                    .collect(Collectors.toList());
                response.setStages(stageResponses);
            }
            // 加载评委数据
            List<ActivityJudgeResponse> judges = loadActivityJudges(id);
            response.setJudges(judges);
            
            return response;
        }
@@ -118,16 +167,31 @@
            }
        }
        
        // 如果是比赛,验证必须至少有一个阶段
        if (input.isMainActivity()) {
            if (input.getStages() == null || input.getStages().isEmpty()) {
                throw new RuntimeException("比赛必须至少有一个阶段");
            }
            // 验证阶段数量不超过5个
            if (input.getStages().size() > 5) {
                throw new RuntimeException("比赛阶段数量不能超过5个");
            }
            // 验证sort_order连续性
            validateSortOrderContinuity(input.getStages());
        }
        // 保存比赛
        activity = activityRepository.save(activity);
        
        // 如果是比赛且有阶段信息,保存阶段
        if (input.isCompetition() && input.getStages() != null && !input.getStages().isEmpty()) {
        if (input.isMainActivity() && input.getStages() != null && !input.getStages().isEmpty()) {
            saveActivityStages(activity.getId(), input.getStages());
        }
        
        // 如果是比赛且有评委信息,保存评委
        if (input.isCompetition() && input.getJudges() != null && !input.getJudges().isEmpty()) {
        if (input.isMainActivity() && input.getJudges() != null && !input.getJudges().isEmpty()) {
            saveActivityJudges(activity.getId(), input.getJudges());
        }
        
@@ -138,9 +202,9 @@
    /**
     * 保存比赛阶段
     */
    private void saveActivityStages(Long competitionId, List<ActivityStageInput> stageInputs) {
    private void saveActivityStages(Long activityId, List<ActivityStageInput> stageInputs) {
        // 获取现有的所有阶段
        List<Activity> existingStages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(competitionId, 1);
        List<Activity> existingStages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(activityId, 1);
        
        // 收集传入的阶段ID
        List<Long> inputStageIds = stageInputs.stream()
@@ -157,11 +221,11 @@
        }
        
        // 获取比赛信息,用于继承报名截止时间和评分模板
        Optional<Activity> competitionOpt = activityRepository.findById(competitionId);
        if (!competitionOpt.isPresent()) {
        Optional<Activity> activityOpt = activityRepository.findById(activityId);
        if (!activityOpt.isPresent()) {
            throw new RuntimeException("比赛不存在");
        }
        Activity competition = competitionOpt.get();
        Activity activity = activityOpt.get();
        
        // 处理传入的阶段(新增或更新)
        for (ActivityStageInput stageInput : stageInputs) {
@@ -170,8 +234,8 @@
            if (stageInput.isNew()) {
                // 新增阶段
                stage = new Activity();
                stage.setPid(competitionId);
                stage.setPath(generatePath(competitionId));
                stage.setPid(activityId);
                stage.setPath(generatePath(activityId));
            } else {
                // 编辑阶段
                Optional<Activity> existingOpt = activityRepository.findById(stageInput.getId());
@@ -187,16 +251,17 @@
            stage.setMatchTime(stageInput.getMatchTime());
            stage.setAddress(stageInput.getAddress());
            stage.setPlayerMax(stageInput.getPlayerMax());
            stage.setSortOrder(stageInput.getSortOrder());
            stage.setState(stageInput.getState());
            
            // 阶段继承比赛的报名截止时间
            stage.setSignupDeadline(competition.getSignupDeadline());
            stage.setSignupDeadline(activity.getSignupDeadline());
            
            // 阶段的评分模板:如果指定了则使用指定的,否则继承比赛的评分模板
            if (stageInput.getRatingSchemeId() != null) {
                stage.setRatingSchemeId(stageInput.getRatingSchemeId());
            } else {
                stage.setRatingSchemeId(competition.getRatingSchemeId());
                stage.setRatingSchemeId(activity.getRatingSchemeId());
            }
            
            activityRepository.save(stage);
@@ -206,36 +271,38 @@
    /**
     * 保存比赛评委
     */
    private void saveActivityJudges(Long competitionId, List<ActivityJudgeInput> judgeInputs) {
    private void saveActivityJudges(Long activityId, List<ActivityJudgeInput> judgeInputs) {
        if (judgeInputs == null || judgeInputs.isEmpty()) {
            return;
        }
        
        // 获取比赛的所有阶段(如果有的话)
        List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(competitionId, 1);
        List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(activityId, 1);
        
        // 保存评委关联
        for (ActivityJudgeInput judgeInput : judgeInputs) {
            // 先删除该评委的现有关联
            activityJudgeRepository.deleteByActivityIdAndJudgeId(competitionId, judgeInput.getJudgeId());
            activityJudgeRepository.deleteByActivityIdAndJudgeId(activityId, judgeInput.getJudgeId());
            
            if (judgeInput.getStageIds() == null || judgeInput.getStageIds().isEmpty()) {
                // 如果没有指定阶段,创建关联到所有阶段(如果有阶段)或直接关联到比赛(如果没有阶段)
                if (stages.isEmpty()) {
                    // 比赛没有阶段,直接关联到比赛(stage_id为null表示所有阶段)
                    ActivityJudge activityJudge = new ActivityJudge(competitionId, judgeInput.getJudgeId(), null);
                    ActivityJudge activityJudge = new ActivityJudge(activityId, judgeInput.getJudgeId(), null);
                    activityJudgeRepository.save(activityJudge);
                } else {
                    // 为每个阶段创建关联
                    for (Activity stage : stages) {
                        ActivityJudge activityJudge = new ActivityJudge(competitionId, judgeInput.getJudgeId(), stage.getId());
                        ActivityJudge activityJudge = new ActivityJudge(activityId, judgeInput.getJudgeId(), stage.getId());
                        activityJudgeRepository.save(activityJudge);
                    }
                }
            } else {
                // 为每个指定的阶段创建关联
                for (Long stageId : judgeInput.getStageIds()) {
                    ActivityJudge activityJudge = new ActivityJudge(competitionId, judgeInput.getJudgeId(), stageId);
                    // 如果stageId等于当前比赛ID,表示评委负责整个比赛,stage_id设为null
                    Long actualStageId = stageId.equals(activityId) ? null : stageId;
                    ActivityJudge activityJudge = new ActivityJudge(activityId, judgeInput.getJudgeId(), actualStageId);
                    activityJudgeRepository.save(activityJudge);
                }
            }
@@ -251,7 +318,7 @@
            Activity activity = activityOpt.get();
            
            // 如果是比赛,先删除其所有阶段
            if (activity.isCompetition()) {
            if (activity.isMainActivity()) {
                List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(id, 1);
                for (Activity stage : stages) {
                    stage.setState(0); // 软删除
@@ -268,61 +335,30 @@
    }
    
    /**
     * 获取所有有效比赛和阶段(用于下拉选择)
     * 获取所有有效主比赛(用于下拉选择)
     */
    public List<ActivityResponse> findAllActivitiesForSelection() {
        // 获取所有活动(包括比赛和阶段)
        List<Activity> activities = activityRepository.findByStateOrderByPidAscNameAsc(1);
        // 获取所有状态为1的活动
        List<Activity> allActivities = activityRepository.findByStateOrderByPidAscNameAsc(1);
        
        // 创建比赛ID到比赛对象的映射,用于快速查找父比赛
        Map<Long, Activity> competitionMap = activities.stream()
        // 过滤:只保留主比赛(pid=0)
        List<Activity> filteredActivities = allActivities.stream()
            .filter(activity -> activity.getPid() == 0)
            .collect(Collectors.toMap(Activity::getId, activity -> activity));
        // 转换为ActivityResponse并填充parent信息
        List<ActivityResponse> result = activities.stream()
            .map(activity -> {
                ActivityResponse response = new ActivityResponse(activity);
                // 如果是阶段(pid > 0),填充parent信息
                if (activity.getPid() > 0) {
                    Activity parentActivity = competitionMap.get(activity.getPid());
                    if (parentActivity != null) {
                        response.setParent(new ActivityResponse(parentActivity));
                    }
                }
                return response;
            })
            .collect(Collectors.toList());
        
        // 自定义排序:比赛和其阶段放在一起
        result.sort((a, b) -> {
            // 如果都是比赛(pid=0),按名称排序
            if (a.getPid() == 0 && b.getPid() == 0) {
                return a.getName().compareTo(b.getName());
            }
            // 如果都是阶段,先按父比赛名称排序,再按阶段名称排序
            if (a.getPid() > 0 && b.getPid() > 0) {
                String aParentName = a.getParent() != null ? a.getParent().getName() : "";
                String bParentName = b.getParent() != null ? b.getParent().getName() : "";
                int parentCompare = aParentName.compareTo(bParentName);
                if (parentCompare != 0) {
                    return parentCompare;
                }
                return a.getName().compareTo(b.getName());
            }
            // 如果一个是比赛,一个是阶段
            if (a.getPid() == 0 && b.getPid() > 0) {
                String bParentName = b.getParent() != null ? b.getParent().getName() : "";
                int compare = a.getName().compareTo(bParentName);
                return compare <= 0 ? -1 : 1; // 比赛排在其阶段前面
            }
            if (a.getPid() > 0 && b.getPid() == 0) {
                String aParentName = a.getParent() != null ? a.getParent().getName() : "";
                int compare = aParentName.compareTo(b.getName());
                return compare < 0 ? -1 : 1; // 阶段排在其比赛后面
            }
            return 0;
        });
        // 转换为ActivityResponse
        List<ActivityResponse> result = filteredActivities.stream()
            .map(activity -> {
                ActivityResponse response = new ActivityResponse(activity);
                // 设置参赛人数(所有阶段的报名数量总和)
                Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
                int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
                response.setPlayerCount(playerCount);
                return response;
            })
            .sorted((a, b) -> a.getName().compareTo(b.getName()))
            .collect(Collectors.toList());
        
        return result;
    }
@@ -330,27 +366,88 @@
    /**
     * 获取比赛的所有阶段
     */
    public List<ActivityResponse> findStagesByCompetitionId(Long competitionId) {
        List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(competitionId, 1);
    public List<ActivityResponse> findStagesByActivityId(Long activityId) {
        List<Activity> stages = activityRepository.findByPidAndStateOrderByCreateTimeAsc(activityId, 1);
        return stages.stream()
            .map(ActivityResponse::new)
            .map(activity -> {
                ActivityResponse response = new ActivityResponse(activity);
                // 设置参赛人数(审核通过的报名数量)
                Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
                int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
                response.setPlayerCount(playerCount);
                return response;
            })
            .collect(Collectors.toList());
    }
    /**
     * 获取所有比赛阶段(用于评审页面下拉选择)
     * 返回所有状态为1且pid>0的比赛阶段
     */
    public List<ActivityResponse> findAllStagesForSelection() {
        // 获取所有状态为1的活动
        List<Activity> allActivities = activityRepository.findByStateOrderByPidAscNameAsc(1);
        // 分离主比赛和阶段
        Map<Long, Activity> parentActivitiesMap = allActivities.stream()
            .filter(activity -> activity.getPid() == 0)
            .collect(Collectors.toMap(Activity::getId, activity -> activity));
        // 过滤:只保留比赛阶段(pid>0)
        List<Activity> filteredStages = allActivities.stream()
            .filter(activity -> activity.getPid() > 0)
            .collect(Collectors.toList());
        // 转换为ActivityResponse
        List<ActivityResponse> result = filteredStages.stream()
            .map(activity -> {
                ActivityResponse response = new ActivityResponse(activity);
                // 设置参赛人数(审核通过的报名数量)
                Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
                int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
                response.setPlayerCount(playerCount);
                // 手动设置parent信息
                Activity parentActivity = parentActivitiesMap.get(activity.getPid());
                if (parentActivity != null) {
                    ActivityResponse parentResponse = new ActivityResponse(parentActivity);
                    response.setParent(parentResponse);
                }
                return response;
            })
            .sorted((a, b) -> {
                // 先按父比赛ID排序,再按阶段名称排序
                int pidCompare = Long.compare(a.getPid(), b.getPid());
                if (pidCompare != 0) return pidCompare;
                return a.getName().compareTo(b.getName());
            })
            .collect(Collectors.toList());
        return result;
    }
    
    /**
     * 统计比赛数量
     */
    public long countActiveCompetitions() {
        return activityRepository.countActiveCompetitions();
    public long countActiveActivities() {
        return activityRepository.countActiveActivities();
    }
    
    /**
     * 获取进行中的比赛
     */
    public List<ActivityResponse> findOngoingCompetitions() {
        List<Activity> activities = activityRepository.findOngoingCompetitions();
    public List<ActivityResponse> findOngoingActivities() {
        List<Activity> activities = activityRepository.findOngoingActivities();
        return activities.stream()
            .map(ActivityResponse::new)
            .map(activity -> {
                ActivityResponse response = new ActivityResponse(activity);
                // 设置参赛人数(审核通过的报名数量)
                Long playerCountLong = activityPlayerRepository.countByActivityId(activity.getId());
                int playerCount = playerCountLong != null ? playerCountLong.intValue() : 0;
                response.setPlayerCount(playerCount);
                return response;
            })
            .collect(Collectors.toList());
    }
    
@@ -368,4 +465,78 @@
            return "/" + pid + "/";
        }
    }
    /**
     * 加载活动的评委数据
     */
    private List<ActivityJudgeResponse> loadActivityJudges(Long activityId) {
        // 查询活动的评委关联
        List<ActivityJudge> activityJudges = activityJudgeRepository.findByActivityId(activityId);
        // 按评委ID分组,收集每个评委负责的阶段
        Map<Long, List<Long>> judgeStageMap = activityJudges.stream()
            .collect(Collectors.groupingBy(
                ActivityJudge::getJudgeId,
                Collectors.mapping(
                    aj -> aj.getStageId() != null ? aj.getStageId() : activityId, // 如果stage_id为null,表示负责整个比赛,使用activityId
                    Collectors.toList()
                )
            ));
        // 查询评委详细信息并构建响应
        List<ActivityJudgeResponse> result = new ArrayList<>();
        for (Map.Entry<Long, List<Long>> entry : judgeStageMap.entrySet()) {
            Long judgeId = entry.getKey();
            List<Long> stageIds = entry.getValue();
            Optional<Judge> judgeOpt = judgeRepository.findById(judgeId);
            if (judgeOpt.isPresent()) {
                Judge judge = judgeOpt.get();
                ActivityJudgeResponse judgeResponse = new ActivityJudgeResponse(
                    judge.getId(),
                    judge.getName(),
                    judge.getPhone(),
                    judge.getDescription(),
                    stageIds
                );
                result.add(judgeResponse);
            }
        }
        return result;
    }
    /**
     * 验证阶段的sort_order连续性
     */
    private void validateSortOrderContinuity(List<ActivityStageInput> stages) {
        if (stages == null || stages.isEmpty()) {
            return;
        }
        // 收集所有的sortOrder值
        List<Integer> sortOrders = stages.stream()
            .map(ActivityStageInput::getSortOrder)
            .filter(Objects::nonNull)
            .sorted()
            .collect(Collectors.toList());
        // 检查是否从1开始
        if (sortOrders.isEmpty() || sortOrders.get(0) != 1) {
            throw new RuntimeException("阶段排序必须从1开始");
        }
        // 检查连续性
        for (int i = 0; i < sortOrders.size(); i++) {
            if (sortOrders.get(i) != i + 1) {
                throw new RuntimeException("阶段排序必须连续,不能跳跃(如:1,2,3...)");
            }
        }
        // 检查是否有重复的sortOrder
        Set<Integer> uniqueSortOrders = new HashSet<>(sortOrders);
        if (uniqueSortOrders.size() != sortOrders.size()) {
            throw new RuntimeException("阶段排序不能重复");
        }
    }
}