diff --git a/eladmin-system/src/main/resources/db/migration/V14__Add_Average_Score_To_Team.sql b/eladmin-system/src/main/resources/db/migration/V14__Add_Average_Score_To_Team.sql new file mode 100644 index 00000000..1b012acc --- /dev/null +++ b/eladmin-system/src/main/resources/db/migration/V14__Add_Average_Score_To_Team.sql @@ -0,0 +1,2 @@ +-- Add average_score column to team table +ALTER TABLE team ADD COLUMN average_score DOUBLE DEFAULT 0.0; diff --git a/sport/src/main/java/com/srr/domain/Event.java b/sport/src/main/java/com/srr/domain/Event.java index 57ff4a7d..bb341a19 100644 --- a/sport/src/main/java/com/srr/domain/Event.java +++ b/sport/src/main/java/com/srr/domain/Event.java @@ -159,6 +159,12 @@ public class Event implements Serializable { inverseJoinColumns = {@JoinColumn(name = "player_id",referencedColumnName = "id")}) private List coHostPlayers = new ArrayList<>(); + @OneToMany(mappedBy = "event") + private List matchGroups = new ArrayList<>(); + + @OneToMany(mappedBy = "event") + private List teams = new ArrayList<>(); + public void copy(Event source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } diff --git a/sport/src/main/java/com/srr/domain/Team.java b/sport/src/main/java/com/srr/domain/Team.java index 488fd593..6a915dcd 100644 --- a/sport/src/main/java/com/srr/domain/Team.java +++ b/sport/src/main/java/com/srr/domain/Team.java @@ -35,6 +35,10 @@ public class Team implements Serializable { @Column(name = "team_size") @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") diff --git a/sport/src/main/java/com/srr/domain/TeamPlayer.java b/sport/src/main/java/com/srr/domain/TeamPlayer.java index 88c6d1d7..61acb544 100644 --- a/sport/src/main/java/com/srr/domain/TeamPlayer.java +++ b/sport/src/main/java/com/srr/domain/TeamPlayer.java @@ -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 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); + } } diff --git a/sport/src/main/java/com/srr/dto/MatchGroupGenerationDto.java b/sport/src/main/java/com/srr/dto/MatchGroupGenerationDto.java new file mode 100644 index 00000000..830bb7f7 --- /dev/null +++ b/sport/src/main/java/com/srr/dto/MatchGroupGenerationDto.java @@ -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; +} diff --git a/sport/src/main/java/com/srr/dto/TeamDto.java b/sport/src/main/java/com/srr/dto/TeamDto.java index b3850122..ded4f069 100644 --- a/sport/src/main/java/com/srr/dto/TeamDto.java +++ b/sport/src/main/java/com/srr/dto/TeamDto.java @@ -44,6 +44,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 teamPlayers; diff --git a/sport/src/main/java/com/srr/dto/TeamPlayerReassignDto.java b/sport/src/main/java/com/srr/dto/TeamPlayerReassignDto.java new file mode 100644 index 00000000..4fad79c3 --- /dev/null +++ b/sport/src/main/java/com/srr/dto/TeamPlayerReassignDto.java @@ -0,0 +1,38 @@ +/* + * 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 TeamPlayerReassignDto { + + @NotNull + @ApiModelProperty(value = "Team player ID to reassign") + private Long teamPlayerId; + + @NotNull + @ApiModelProperty(value = "Target team ID") + private Long targetTeamId; +} diff --git a/sport/src/main/java/com/srr/repository/TeamPlayerRepository.java b/sport/src/main/java/com/srr/repository/TeamPlayerRepository.java index b833aeed..881b4972 100644 --- a/sport/src/main/java/com/srr/repository/TeamPlayerRepository.java +++ b/sport/src/main/java/com/srr/repository/TeamPlayerRepository.java @@ -18,6 +18,10 @@ package com.srr.repository; import com.srr.domain.TeamPlayer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; /** * @website https://eladmin.vip @@ -26,4 +30,9 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; **/ public interface TeamPlayerRepository extends JpaRepository, JpaSpecificationExecutor { boolean existsByTeamIdAndPlayerId(Long teamId, Long playerId); + + TeamPlayer findByTeamIdAndPlayerId(Long teamId, Long playerId); + + @Query("SELECT tp FROM TeamPlayer tp JOIN tp.team t JOIN t.event e WHERE e.id = :eventId") + List findByEventId(@Param("eventId") Long eventId); } diff --git a/sport/src/main/java/com/srr/rest/EventController.java b/sport/src/main/java/com/srr/rest/EventController.java index 695509f4..c4a9f89e 100644 --- a/sport/src/main/java/com/srr/rest/EventController.java +++ b/sport/src/main/java/com/srr/rest/EventController.java @@ -19,8 +19,12 @@ 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; import io.swagger.annotations.ApiParam; @@ -36,6 +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 @@ -49,6 +56,8 @@ import java.io.IOException; public class EventController { private final EventService eventService; + private final TeamPlayerService teamPlayerService; + private final MatchGroupService matchGroupService; @ApiOperation("Export Data") @GetMapping(value = "/download") @@ -104,6 +113,27 @@ public class EventController { return new ResponseEntity<>(result, HttpStatus.OK); } + @GetMapping("/{id}/players") + @ApiOperation("Find all team players in an event") + @PreAuthorize("@el.check('event:list')") + public ResponseEntity> findEventPlayers(@PathVariable("id") Long eventId) { + 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 generateMatchGroups(@Validated @RequestBody MatchGroupGenerationDto dto) { + Integer groupsCreated = matchGroupService.generateMatchGroups(dto); + + Map 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") diff --git a/sport/src/main/java/com/srr/rest/TeamPlayerController.java b/sport/src/main/java/com/srr/rest/TeamPlayerController.java new file mode 100644 index 00000000..bad7d72c --- /dev/null +++ b/sport/src/main/java/com/srr/rest/TeamPlayerController.java @@ -0,0 +1,66 @@ +/* + * 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.rest; + +import com.srr.dto.TeamPlayerDto; +import com.srr.dto.TeamPlayerReassignDto; +import com.srr.service.TeamPlayerService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import me.zhengjie.annotation.Log; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * @author Chanheng + * @website https://eladmin.vip + * @date 2025-05-26 + **/ +@RestController +@RequiredArgsConstructor +@Api(tags = "Team Player Management") +@RequestMapping("/api/team-player") +public class TeamPlayerController { + + private final TeamPlayerService teamPlayerService; + + @GetMapping("/{id}") + @ApiOperation("Get team player details") + @PreAuthorize("@el.check('event:list')") + public ResponseEntity getTeamPlayer(@PathVariable Long id) { + return new ResponseEntity<>(teamPlayerService.findById(id), HttpStatus.OK); + } + + @PutMapping("/{id}/check-in") + @Log("Check in player") + @ApiOperation("Check in player for an event") + @PreAuthorize("@el.check('event:edit')") + public ResponseEntity checkIn(@PathVariable Long id) { + return new ResponseEntity<>(teamPlayerService.checkIn(id), HttpStatus.OK); + } + + @PostMapping("/reassign") + @Log("Reassign player to another team") + @ApiOperation("Reassign player to another team") + @PreAuthorize("@el.check('event:admin')") + public ResponseEntity reassignPlayer(@Validated @RequestBody TeamPlayerReassignDto dto) { + return new ResponseEntity<>(teamPlayerService.reassignPlayer(dto), HttpStatus.OK); + } +} diff --git a/sport/src/main/java/com/srr/service/MatchGroupService.java b/sport/src/main/java/com/srr/service/MatchGroupService.java new file mode 100644 index 00000000..0402c1bf --- /dev/null +++ b/sport/src/main/java/com/srr/service/MatchGroupService.java @@ -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); +} diff --git a/sport/src/main/java/com/srr/service/TeamPlayerService.java b/sport/src/main/java/com/srr/service/TeamPlayerService.java new file mode 100644 index 00000000..5da3698d --- /dev/null +++ b/sport/src/main/java/com/srr/service/TeamPlayerService.java @@ -0,0 +1,61 @@ +/* + * 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.domain.TeamPlayer; +import com.srr.dto.TeamPlayerDto; +import com.srr.dto.TeamPlayerReassignDto; +import org.springframework.data.domain.Pageable; +import me.zhengjie.utils.PageResult; + +import java.util.List; + +/** + * @author Chanheng + * @website https://eladmin.vip + * @description Service interface for TeamPlayer + * @date 2025-05-26 + **/ +public interface TeamPlayerService { + + /** + * Get a specific TeamPlayer by ID + * @param id TeamPlayer ID + * @return TeamPlayerDto + */ + TeamPlayerDto findById(Long id); + + /** + * Check in a player for an event + * @param id TeamPlayer ID + * @return The updated TeamPlayerDto + */ + TeamPlayerDto checkIn(Long id); + + /** + * Find all TeamPlayers by event ID + * @param eventId the event ID + * @return List of TeamPlayerDto objects + */ + List findByEventId(Long eventId); + + /** + * Reassign a player from one team to another + * @param dto Contains teamPlayerId and targetTeamId + * @return The updated TeamPlayerDto + */ + TeamPlayerDto reassignPlayer(TeamPlayerReassignDto dto); +} diff --git a/sport/src/main/java/com/srr/service/impl/MatchGroupServiceImpl.java b/sport/src/main/java/com/srr/service/impl/MatchGroupServiceImpl.java new file mode 100644 index 00000000..4bc3831c --- /dev/null +++ b/sport/src/main/java/com/srr/service/impl/MatchGroupServiceImpl.java @@ -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 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 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 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> teamGroups = createTeamGroups(sortedTeams, targetGroupCount); + + // Create match groups + int groupCount = 0; + for (List teamGroup : teamGroups) { + if (!teamGroup.isEmpty()) { + createMatchGroup(event, teamGroup, "Group " + (++groupCount), teamGroup.size()); + } + } + + return groupCount; + } + + /** + * Group teams based strictly on their score order + */ + private List> createTeamGroups(List 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> 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 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); + } + } +} diff --git a/sport/src/main/java/com/srr/service/impl/TeamPlayerServiceImpl.java b/sport/src/main/java/com/srr/service/impl/TeamPlayerServiceImpl.java new file mode 100644 index 00000000..0c9da130 --- /dev/null +++ b/sport/src/main/java/com/srr/service/impl/TeamPlayerServiceImpl.java @@ -0,0 +1,117 @@ +/* + * 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.Team; +import com.srr.domain.TeamPlayer; +import com.srr.dto.TeamPlayerDto; +import com.srr.dto.TeamPlayerReassignDto; +import com.srr.dto.mapstruct.TeamPlayerMapper; +import com.srr.repository.TeamPlayerRepository; +import com.srr.repository.TeamRepository; +import com.srr.service.TeamPlayerService; +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.List; +import java.util.stream.Collectors; + +/** + * @author Chanheng + * @website https://eladmin.vip + * @date 2025-05-26 + **/ +@Service +@RequiredArgsConstructor +public class TeamPlayerServiceImpl implements TeamPlayerService { + + private final TeamPlayerRepository teamPlayerRepository; + private final TeamRepository teamRepository; + private final TeamPlayerMapper teamPlayerMapper; + + @Override + @Transactional(readOnly = true) + public TeamPlayerDto findById(Long id) { + TeamPlayer teamPlayer = teamPlayerRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException(TeamPlayer.class, "id", id.toString())); + return teamPlayerMapper.toDto(teamPlayer); + } + + @Override + @Transactional + public TeamPlayerDto checkIn(Long id) { + TeamPlayer teamPlayer = teamPlayerRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException(TeamPlayer.class, "id", id.toString())); + + if (teamPlayer.isCheckedIn()) { + throw new BadRequestException("Player is already checked in"); + } + + teamPlayer.setCheckedIn(true); + teamPlayerRepository.save(teamPlayer); + + return teamPlayerMapper.toDto(teamPlayer); + } + + @Override + @Transactional(readOnly = true) + public List findByEventId(Long eventId) { + List teamPlayers = teamPlayerRepository.findByEventId(eventId); + return teamPlayers.stream() + .map(teamPlayerMapper::toDto) + .collect(Collectors.toList()); + } + + @Override + @Transactional + public TeamPlayerDto reassignPlayer(TeamPlayerReassignDto dto) { + // Find the team player to reassign + TeamPlayer teamPlayer = teamPlayerRepository.findById(dto.getTeamPlayerId()) + .orElseThrow(() -> new EntityNotFoundException(TeamPlayer.class, "id", dto.getTeamPlayerId().toString())); + + // Find the target team + Team targetTeam = teamRepository.findById(dto.getTargetTeamId()) + .orElseThrow(() -> new EntityNotFoundException(Team.class, "id", dto.getTargetTeamId().toString())); + + // Store the original team for potential deletion + Team originalTeam = teamPlayer.getTeam(); + + // Check if target team is in the same event as the original team + if (!originalTeam.getEvent().getId().equals(targetTeam.getEvent().getId())) { + throw new BadRequestException("Cannot reassign player to a team in a different event"); + } + + // Check if the target team is full + if (targetTeam.getTeamPlayers().size() >= targetTeam.getTeamSize()) { + throw new BadRequestException("Target team is already full"); + } + + // Reassign the player to the new team + teamPlayer.setTeam(targetTeam); + teamPlayerRepository.save(teamPlayer); + + // Check if original team is now empty, and delete if it is + if (originalTeam.getTeamPlayers().stream() + .noneMatch(tp -> !tp.getId().equals(teamPlayer.getId()))) { + teamRepository.delete(originalTeam); + } + + return teamPlayerMapper.toDto(teamPlayer); + } +}