mirror of https://github.com/elunez/eladmin
re-assign
parent
f910c6590b
commit
9cc769c1e3
|
@ -0,0 +1,2 @@
|
|||
-- Add average_score column to team table
|
||||
ALTER TABLE team ADD COLUMN average_score DOUBLE DEFAULT 0.0;
|
|
@ -159,6 +159,12 @@ public class Event implements Serializable {
|
|||
inverseJoinColumns = {@JoinColumn(name = "player_id",referencedColumnName = "id")})
|
||||
private List<Player> coHostPlayers = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "event")
|
||||
private List<MatchGroup> matchGroups = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "event")
|
||||
private List<Team> teams = new ArrayList<>();
|
||||
|
||||
public void copy(Event source){
|
||||
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
|
||||
}
|
||||
|
|
|
@ -36,6 +36,10 @@ public class Team implements Serializable {
|
|||
@ApiModelProperty(value = "Team size")
|
||||
private int teamSize;
|
||||
|
||||
@Column(name = "average_score")
|
||||
@ApiModelProperty(value = "Average team score based on player scores")
|
||||
private Double averageScore = 0.0;
|
||||
|
||||
@OneToMany(mappedBy = "team")
|
||||
@ApiModelProperty(value = "teamPlayers")
|
||||
private List<TeamPlayer> teamPlayers;
|
||||
|
|
|
@ -6,6 +6,7 @@ import lombok.Setter;
|
|||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
@ -31,4 +32,37 @@ public class TeamPlayer implements Serializable {
|
|||
|
||||
@Column(name = "is_checked_in")
|
||||
private boolean isCheckedIn;
|
||||
|
||||
@PrePersist
|
||||
@PreUpdate
|
||||
public void updateTeamScore() {
|
||||
if (team != null) {
|
||||
calculateAndUpdateTeamScore(team);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and updates the average score for the team
|
||||
* @param team The team to update the score for
|
||||
*/
|
||||
private void calculateAndUpdateTeamScore(Team team) {
|
||||
List<TeamPlayer> players = team.getTeamPlayers();
|
||||
if (players == null || players.isEmpty()) {
|
||||
team.setAverageScore(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
double totalScore = 0;
|
||||
int playerCount = 0;
|
||||
|
||||
for (TeamPlayer player : players) {
|
||||
if (player.getScore() != null) {
|
||||
totalScore += player.getScore();
|
||||
playerCount++;
|
||||
}
|
||||
}
|
||||
|
||||
double averageScore = playerCount > 0 ? totalScore / playerCount : 0;
|
||||
team.setAverageScore(averageScore);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2019-2025 Zheng Jie
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.srr.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author Chanheng
|
||||
* @website https://eladmin.vip
|
||||
* @date 2025-05-26
|
||||
**/
|
||||
@Data
|
||||
public class MatchGroupGenerationDto {
|
||||
|
||||
@NotNull
|
||||
@ApiModelProperty(value = "Event ID")
|
||||
private Long eventId;
|
||||
}
|
|
@ -45,6 +45,9 @@ public class TeamDto implements Serializable {
|
|||
@ApiModelProperty(value = "Team size")
|
||||
private Integer teamSize;
|
||||
|
||||
@ApiModelProperty(value = "Average team score based on player scores")
|
||||
private Double averageScore;
|
||||
|
||||
@ApiModelProperty(value = "Team players")
|
||||
private List<TeamPlayerDto> teamPlayers;
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@ import com.srr.domain.Event;
|
|||
import com.srr.dto.EventDto;
|
||||
import com.srr.dto.EventQueryCriteria;
|
||||
import com.srr.dto.JoinEventDto;
|
||||
import com.srr.dto.MatchGroupGenerationDto;
|
||||
import com.srr.dto.TeamPlayerDto;
|
||||
import com.srr.enumeration.EventStatus;
|
||||
import com.srr.service.EventService;
|
||||
import com.srr.service.MatchGroupService;
|
||||
import com.srr.service.TeamPlayerService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
|
@ -38,7 +40,9 @@ import org.springframework.web.bind.annotation.*;
|
|||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Chanheng
|
||||
|
@ -53,6 +57,7 @@ public class EventController {
|
|||
|
||||
private final EventService eventService;
|
||||
private final TeamPlayerService teamPlayerService;
|
||||
private final MatchGroupService matchGroupService;
|
||||
|
||||
@ApiOperation("Export Data")
|
||||
@GetMapping(value = "/download")
|
||||
|
@ -115,6 +120,20 @@ public class EventController {
|
|||
return new ResponseEntity<>(teamPlayerService.findByEventId(eventId), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@PostMapping("/generate-groups")
|
||||
@Log("Generate match groups")
|
||||
@ApiOperation("Generate match groups based on team scores")
|
||||
@PreAuthorize("@el.check('event:admin')")
|
||||
public ResponseEntity<Object> generateMatchGroups(@Validated @RequestBody MatchGroupGenerationDto dto) {
|
||||
Integer groupsCreated = matchGroupService.generateMatchGroups(dto);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("groupsCreated", groupsCreated);
|
||||
result.put("message", "Successfully created " + groupsCreated + " match groups based on team scores");
|
||||
|
||||
return new ResponseEntity<>(result, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Log("Delete event")
|
||||
@ApiOperation("Delete event")
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2019-2025 Zheng Jie
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.srr.service;
|
||||
|
||||
import com.srr.dto.MatchGroupGenerationDto;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Chanheng
|
||||
* @website https://eladmin.vip
|
||||
* @date 2025-05-26
|
||||
**/
|
||||
public interface MatchGroupService {
|
||||
|
||||
/**
|
||||
* Generate match groups for teams in an event based on score similarity
|
||||
* @param dto parameters for group generation
|
||||
* @return Number of groups created
|
||||
*/
|
||||
Integer generateMatchGroups(MatchGroupGenerationDto dto);
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2019-2025 Zheng Jie
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.srr.service.impl;
|
||||
|
||||
import com.srr.domain.Event;
|
||||
import com.srr.domain.MatchGroup;
|
||||
import com.srr.domain.Team;
|
||||
import com.srr.dto.MatchGroupGenerationDto;
|
||||
import com.srr.repository.EventRepository;
|
||||
import com.srr.repository.MatchGroupRepository;
|
||||
import com.srr.repository.TeamRepository;
|
||||
import com.srr.service.MatchGroupService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.zhengjie.exception.BadRequestException;
|
||||
import me.zhengjie.exception.EntityNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Chanheng
|
||||
* @website https://eladmin.vip
|
||||
* @date 2025-05-26
|
||||
**/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MatchGroupServiceImpl implements MatchGroupService {
|
||||
|
||||
private final EventRepository eventRepository;
|
||||
private final TeamRepository teamRepository;
|
||||
private final MatchGroupRepository matchGroupRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Integer generateMatchGroups(MatchGroupGenerationDto dto) {
|
||||
// Find the event
|
||||
Event event = eventRepository.findById(dto.getEventId())
|
||||
.orElseThrow(() -> new EntityNotFoundException(Event.class, "id", dto.getEventId().toString()));
|
||||
|
||||
// Check if the event has a group count
|
||||
if (event.getGroupCount() == null || event.getGroupCount() <= 0) {
|
||||
throw new BadRequestException("Event has no valid group count defined");
|
||||
}
|
||||
|
||||
// Get all teams for the event
|
||||
List<Team> teams = event.getTeams();
|
||||
|
||||
if (teams.isEmpty()) {
|
||||
throw new BadRequestException("No teams found for event with ID: " + dto.getEventId());
|
||||
}
|
||||
|
||||
// Clear existing match groups for this event
|
||||
List<MatchGroup> existingGroups = event.getMatchGroups();
|
||||
if (existingGroups != null && !existingGroups.isEmpty()) {
|
||||
for (MatchGroup group : existingGroups) {
|
||||
// Detach teams from groups
|
||||
for (Team team : group.getTeams()) {
|
||||
team.setMatchGroup(null);
|
||||
}
|
||||
matchGroupRepository.delete(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort teams by their average score (which is now stored on the Team entity)
|
||||
List<Team> sortedTeams = teams.stream()
|
||||
.sorted(Comparator.comparing(Team::getAverageScore, Comparator.nullsLast(Comparator.naturalOrder())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Group teams based on their sorted order and the target group count
|
||||
int targetGroupCount = event.getGroupCount();
|
||||
List<List<Team>> teamGroups = createTeamGroups(sortedTeams, targetGroupCount);
|
||||
|
||||
// Create match groups
|
||||
int groupCount = 0;
|
||||
for (List<Team> teamGroup : teamGroups) {
|
||||
if (!teamGroup.isEmpty()) {
|
||||
createMatchGroup(event, teamGroup, "Group " + (++groupCount), teamGroup.size());
|
||||
}
|
||||
}
|
||||
|
||||
return groupCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group teams based strictly on their score order
|
||||
*/
|
||||
private List<List<Team>> createTeamGroups(List<Team> sortedTeams, int targetGroupCount) {
|
||||
int totalTeams = sortedTeams.size();
|
||||
|
||||
// Don't create more groups than we have teams
|
||||
int actualGroupCount = Math.min(targetGroupCount, totalTeams);
|
||||
|
||||
// Initialize the groups
|
||||
List<List<Team>> groups = new ArrayList<>(actualGroupCount);
|
||||
for (int i = 0; i < actualGroupCount; i++) {
|
||||
groups.add(new ArrayList<>());
|
||||
}
|
||||
|
||||
// Distribute teams to groups in a round-robin fashion based on their sorted order
|
||||
// Teams with similar scores will naturally be placed in different groups
|
||||
for (int i = 0; i < sortedTeams.size(); i++) {
|
||||
Team team = sortedTeams.get(i);
|
||||
// Use modulo to distribute teams evenly among groups
|
||||
int groupIndex = i % actualGroupCount;
|
||||
groups.get(groupIndex).add(team);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a match group from a list of teams
|
||||
*/
|
||||
private void createMatchGroup(Event event, List<Team> teams, String name, int groupTeamSize) {
|
||||
MatchGroup matchGroup = new MatchGroup();
|
||||
matchGroup.setName(name);
|
||||
matchGroup.setEvent(event);
|
||||
matchGroup.setGroupTeamSize(groupTeamSize);
|
||||
|
||||
// Save the match group first
|
||||
matchGroup = matchGroupRepository.save(matchGroup);
|
||||
|
||||
// Update the teams with the match group
|
||||
for (Team team : teams) {
|
||||
team.setMatchGroup(matchGroup);
|
||||
teamRepository.save(team);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue