mirror of https://github.com/elunez/eladmin
commit
4b4510bfa6
|
@ -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")})
|
inverseJoinColumns = {@JoinColumn(name = "player_id",referencedColumnName = "id")})
|
||||||
private List<Player> coHostPlayers = new ArrayList<>();
|
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){
|
public void copy(Event source){
|
||||||
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
|
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,10 @@ public class Team implements Serializable {
|
||||||
@Column(name = "team_size")
|
@Column(name = "team_size")
|
||||||
@ApiModelProperty(value = "Team size")
|
@ApiModelProperty(value = "Team size")
|
||||||
private int teamSize;
|
private int teamSize;
|
||||||
|
|
||||||
|
@Column(name = "average_score")
|
||||||
|
@ApiModelProperty(value = "Average team score based on player scores")
|
||||||
|
private Double averageScore = 0.0;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "team")
|
@OneToMany(mappedBy = "team")
|
||||||
@ApiModelProperty(value = "teamPlayers")
|
@ApiModelProperty(value = "teamPlayers")
|
||||||
|
|
|
@ -6,6 +6,7 @@ import lombok.Setter;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
|
@ -31,4 +32,37 @@ public class TeamPlayer implements Serializable {
|
||||||
|
|
||||||
@Column(name = "is_checked_in")
|
@Column(name = "is_checked_in")
|
||||||
private boolean isCheckedIn;
|
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;
|
||||||
|
}
|
|
@ -44,6 +44,9 @@ public class TeamDto implements Serializable {
|
||||||
|
|
||||||
@ApiModelProperty(value = "Team size")
|
@ApiModelProperty(value = "Team size")
|
||||||
private Integer teamSize;
|
private Integer teamSize;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "Average team score based on player scores")
|
||||||
|
private Double averageScore;
|
||||||
|
|
||||||
@ApiModelProperty(value = "Team players")
|
@ApiModelProperty(value = "Team players")
|
||||||
private List<TeamPlayerDto> teamPlayers;
|
private List<TeamPlayerDto> teamPlayers;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -18,6 +18,10 @@ package com.srr.repository;
|
||||||
import com.srr.domain.TeamPlayer;
|
import com.srr.domain.TeamPlayer;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
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
|
* @website https://eladmin.vip
|
||||||
|
@ -26,4 +30,9 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
**/
|
**/
|
||||||
public interface TeamPlayerRepository extends JpaRepository<TeamPlayer, Long>, JpaSpecificationExecutor<TeamPlayer> {
|
public interface TeamPlayerRepository extends JpaRepository<TeamPlayer, Long>, JpaSpecificationExecutor<TeamPlayer> {
|
||||||
boolean existsByTeamIdAndPlayerId(Long teamId, Long playerId);
|
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<TeamPlayer> findByEventId(@Param("eventId") Long eventId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,12 @@ import com.srr.domain.Event;
|
||||||
import com.srr.dto.EventDto;
|
import com.srr.dto.EventDto;
|
||||||
import com.srr.dto.EventQueryCriteria;
|
import com.srr.dto.EventQueryCriteria;
|
||||||
import com.srr.dto.JoinEventDto;
|
import com.srr.dto.JoinEventDto;
|
||||||
|
import com.srr.dto.MatchGroupGenerationDto;
|
||||||
|
import com.srr.dto.TeamPlayerDto;
|
||||||
import com.srr.enumeration.EventStatus;
|
import com.srr.enumeration.EventStatus;
|
||||||
import com.srr.service.EventService;
|
import com.srr.service.EventService;
|
||||||
|
import com.srr.service.MatchGroupService;
|
||||||
|
import com.srr.service.TeamPlayerService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import io.swagger.annotations.ApiParam;
|
import io.swagger.annotations.ApiParam;
|
||||||
|
@ -36,6 +40,9 @@ import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Chanheng
|
* @author Chanheng
|
||||||
|
@ -49,6 +56,8 @@ import java.io.IOException;
|
||||||
public class EventController {
|
public class EventController {
|
||||||
|
|
||||||
private final EventService eventService;
|
private final EventService eventService;
|
||||||
|
private final TeamPlayerService teamPlayerService;
|
||||||
|
private final MatchGroupService matchGroupService;
|
||||||
|
|
||||||
@ApiOperation("Export Data")
|
@ApiOperation("Export Data")
|
||||||
@GetMapping(value = "/download")
|
@GetMapping(value = "/download")
|
||||||
|
@ -104,6 +113,27 @@ public class EventController {
|
||||||
return new ResponseEntity<>(result, HttpStatus.OK);
|
return new ResponseEntity<>(result, HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}/players")
|
||||||
|
@ApiOperation("Find all team players in an event")
|
||||||
|
@PreAuthorize("@el.check('event:list')")
|
||||||
|
public ResponseEntity<List<TeamPlayerDto>> 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<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
|
@DeleteMapping
|
||||||
@Log("Delete event")
|
@Log("Delete event")
|
||||||
@ApiOperation("Delete event")
|
@ApiOperation("Delete event")
|
||||||
|
|
|
@ -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<TeamPlayerDto> 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<TeamPlayerDto> 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<TeamPlayerDto> reassignPlayer(@Validated @RequestBody TeamPlayerReassignDto dto) {
|
||||||
|
return new ResponseEntity<>(teamPlayerService.reassignPlayer(dto), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,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<TeamPlayerDto> 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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<TeamPlayerDto> findByEventId(Long eventId) {
|
||||||
|
List<TeamPlayer> 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue