add waitlist

pull/882/head
chanhengseang 2025-05-26 11:59:22 -07:00
parent 186b685ab5
commit 81c8c2cf4e
17 changed files with 866 additions and 12 deletions

View File

@ -1,4 +1,6 @@
-- Add tags column to event table -- Add tags column to event table
ALTER TABLE event ALTER TABLE event
add column poster_image varchar(255), ADD COLUMN poster_image VARCHAR(255),
ADD COLUMN tags VARCHAR(255) NULL COMMENT 'Tags stored as comma-delimited string'; ADD COLUMN tags VARCHAR(255) NULL COMMENT 'Tags stored as comma-delimited string',
ADD COLUMN current_participants INT DEFAULT 0 COMMENT 'Current number of participants',
ADD COLUMN max_participants INT NULL COMMENT 'Maximum number of participants';

View File

@ -0,0 +1,18 @@
-- Create wait list table for tracking players waiting to join events
CREATE TABLE IF NOT EXISTS wait_list (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
event_id BIGINT NOT NULL COMMENT 'Reference to event',
player_id BIGINT NOT NULL COMMENT 'Reference to player',
notes VARCHAR(255) NULL COMMENT 'Notes',
status VARCHAR(20) NOT NULL DEFAULT 'WAITING' COMMENT 'Status (WAITING, PROMOTED, CANCELLED, EXPIRED)',
create_time DATETIME NULL COMMENT 'Creation time',
update_time DATETIME NULL COMMENT 'Update time',
CONSTRAINT fk_wait_list_event FOREIGN KEY (event_id) REFERENCES event (id),
CONSTRAINT fk_wait_list_player FOREIGN KEY (player_id) REFERENCES player (id),
UNIQUE KEY uk_wait_list_event_player (event_id, player_id)
) ENGINE = InnoDB COMMENT 'Event wait list';
-- Add index for faster queries
CREATE INDEX idx_wait_list_event ON wait_list (event_id);
CREATE INDEX idx_wait_list_player ON wait_list (player_id);
CREATE INDEX idx_wait_list_status ON wait_list (status);

View File

@ -41,7 +41,6 @@ create table event
name varchar(32) not null comment '名称', name varchar(32) not null comment '名称',
description varchar(255) null comment '描述', description varchar(255) null comment '描述',
format enum ('SINGLE', 'DOUBLE', 'TEAM') not null, format enum ('SINGLE', 'DOUBLE', 'TEAM') not null,
max_player int null comment '最大人数',
location varchar(255) null comment '位置', location varchar(255) null comment '位置',
image varchar(255) null comment '图片', image varchar(255) null comment '图片',
create_time datetime null comment '创建时间', create_time datetime null comment '创建时间',

View File

@ -68,10 +68,6 @@ public class Event implements Serializable {
@ApiModelProperty(value = "SINGLE, DOUBLE") @ApiModelProperty(value = "SINGLE, DOUBLE")
private Format format; private Format format;
@Column(name = "`max_player`")
@ApiModelProperty(value = "Maximum number of people")
private Integer maxPlayer;
@Column(name = "`location`") @Column(name = "`location`")
@ApiModelProperty(value = "Location") @ApiModelProperty(value = "Location")
private String location; private String location;
@ -140,6 +136,14 @@ public class Event implements Serializable {
@Column(name = "`allow_wait_list`") @Column(name = "`allow_wait_list`")
private boolean allowWaitList; private boolean allowWaitList;
@Column(name = "`current_participants`")
@ApiModelProperty(value = "Current number of participants")
private Integer currentParticipants = 0;
@Column(name = "`max_participants`")
@ApiModelProperty(value = "Maximum number of participants")
private Integer maxParticipants;
@Column(name = "`poster_image`") @Column(name = "`poster_image`")
private String posterImage; private String posterImage;

View File

@ -0,0 +1,85 @@
/*
* 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.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.srr.enumeration.WaitListStatus;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* Event Wait List Entity
* @author Chanheng
* @date 2025-05-26
*/
@Entity
@Getter
@Setter
@Accessors(chain = true)
@Table(name = "wait_list")
public class WaitList implements Serializable {
@Id
@Column(name = "id")
@ApiModelProperty(value = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "event_id", nullable = false)
@NotNull
@ApiModelProperty(value = "Event ID")
private Long eventId;
@Column(name = "player_id", nullable = false)
@NotNull
@ApiModelProperty(value = "Player ID")
private Long playerId;
@Column(name = "notes")
@ApiModelProperty(value = "Notes")
private String notes;
@Enumerated(EnumType.STRING)
@Column(name = "status")
@ApiModelProperty(value = "Status (WAITING, PROMOTED, CANCELLED, EXPIRED)")
private WaitListStatus status = WaitListStatus.WAITING;
@Column(name = "create_time")
@CreationTimestamp
@ApiModelProperty(value = "Creation Time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Timestamp createTime;
@Column(name = "update_time")
@UpdateTimestamp
@ApiModelProperty(value = "Update Time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Timestamp updateTime;
public void copy(WaitList source) {
this.notes = source.getNotes();
this.status = source.getStatus();
}
}

View File

@ -45,9 +45,6 @@ public class EventDto implements Serializable {
@ApiModelProperty(value = "SINGLE, DOUBLE") @ApiModelProperty(value = "SINGLE, DOUBLE")
private Format format; private Format format;
@ApiModelProperty(value = "最大人数")
private Integer maxPlayer;
@ApiModelProperty(value = "位置") @ApiModelProperty(value = "位置")
private String location; private String location;
@ -95,6 +92,12 @@ public class EventDto implements Serializable {
private String posterImage; private String posterImage;
@ApiModelProperty(value = "Current number of participants")
private Integer currentParticipants;
@ApiModelProperty(value = "Maximum number of participants")
private Integer maxParticipants;
@ApiModelProperty(value = "Co-host players") @ApiModelProperty(value = "Co-host players")
private List<PlayerDto> coHostPlayers; private List<PlayerDto> coHostPlayers;

View File

@ -1,13 +1,43 @@
/*
* 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; package com.srr.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
/**
* @author Chanheng
* @date 2025-05-26
*/
@Data @Data
public class JoinEventDto { public class JoinEventDto {
private Long playerId;
@ApiModelProperty(value = "Event ID")
@NotNull @NotNull
private Long eventId; private Long eventId;
@ApiModelProperty(value = "Player ID")
@NotNull
private Long playerId;
@ApiModelProperty(value = "Team ID (optional)")
private Long teamId; private Long teamId;
@ApiModelProperty(value = "Join as wait list")
private Boolean joinWaitList = false;
} }

View File

@ -0,0 +1,65 @@
/*
* 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 com.fasterxml.jackson.annotation.JsonFormat;
import com.srr.enumeration.WaitListStatus;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* @author Chanheng
* @date 2025-05-26
* @description Wait list DTO for transferring wait list data
*/
@Getter
@Setter
public class WaitListDto implements Serializable {
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "Event ID")
private Long eventId;
@ApiModelProperty(value = "Player ID")
private Long playerId;
@ApiModelProperty(value = "Notes")
private String notes;
@ApiModelProperty(value = "Status (WAITING, PROMOTED, CANCELLED, EXPIRED)")
private WaitListStatus status;
@ApiModelProperty(value = "Creation time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Timestamp createTime;
@ApiModelProperty(value = "Update time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Timestamp updateTime;
// Additional relationships can be added if needed
@ApiModelProperty(value = "Player")
private PlayerDto player;
@ApiModelProperty(value = "Event")
private EventDto event;
}

View File

@ -0,0 +1,51 @@
/*
* 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 com.srr.enumeration.WaitListStatus;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import me.zhengjie.annotation.Query;
import java.sql.Timestamp;
import java.util.List;
/**
* @author Chanheng
* @date 2025-05-26
*/
@Data
public class WaitListQueryCriteria {
@Query
private Long id;
@Query
@ApiModelProperty(value = "Event ID")
private Long eventId;
@Query
@ApiModelProperty(value = "Player ID")
private Long playerId;
@Query
@ApiModelProperty(value = "Status")
private WaitListStatus status;
@Query(type = Query.Type.BETWEEN)
@ApiModelProperty(value = "Create time range")
private List<Timestamp> createTime;
}

View File

@ -0,0 +1,36 @@
/*
* 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.enumeration;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Enum representing the possible states of a wait list entry
* @author Chanheng
* @date 2025-05-26
*/
@Getter
@AllArgsConstructor
public enum WaitListStatus {
WAITING("Waiting"),
PROMOTED("Promoted to participant"),
CANCELLED("Cancelled by player"),
EXPIRED("Expired due to event start");
private final String description;
}

View File

@ -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.repository;
import com.srr.domain.WaitList;
import com.srr.enumeration.WaitListStatus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
/**
* @author Chanheng
* @date 2025-05-26
*/
public interface WaitListRepository extends JpaRepository<WaitList, Long>, JpaSpecificationExecutor<WaitList> {
/**
* Find all wait list entries for an event
* @param eventId The event ID
* @return List of wait list entries
*/
List<WaitList> findByEventId(Long eventId);
/**
* Find all wait list entries for a player
* @param playerId The player ID
* @return List of wait list entries
*/
List<WaitList> findByPlayerId(Long playerId);
/**
* Find wait list entry for a specific player and event
* @param eventId The event ID
* @param playerId The player ID
* @return WaitList entry if exists
*/
WaitList findByEventIdAndPlayerId(Long eventId, Long playerId);
/**
* Find wait list entries by status
* @param status The status (WAITING, PROMOTED, CANCELLED, EXPIRED)
* @return List of wait list entries
*/
List<WaitList> findByStatus(WaitListStatus status);
/**
* Count wait list entries for an event
* @param eventId The event ID
* @return Count of wait list entries
*/
long countByEventId(Long eventId);
}

View File

@ -93,7 +93,13 @@ public class EventController {
return new ResponseEntity<>(result, HttpStatus.OK); return new ResponseEntity<>(result, HttpStatus.OK);
} }
@PostMapping("/{id}/join")
@Log("Join event")
@ApiOperation("Join event")
@PreAuthorize("@el.check('event:join')")
public ResponseEntity<Object> joinEvent(@PathVariable Long id, @RequestBody JoinEventDto joinEventDto) { public ResponseEntity<Object> joinEvent(@PathVariable Long id, @RequestBody JoinEventDto joinEventDto) {
// Ensure ID in path matches ID in DTO
joinEventDto.setEventId(id);
final EventDto result = eventService.joinEvent(joinEventDto); final EventDto result = eventService.joinEvent(joinEventDto);
return new ResponseEntity<>(result, HttpStatus.OK); return new ResponseEntity<>(result, HttpStatus.OK);
} }

View File

@ -0,0 +1,108 @@
/*
* 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.domain.WaitList;
import com.srr.dto.WaitListDto;
import com.srr.dto.WaitListQueryCriteria;
import com.srr.service.WaitListService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import me.zhengjie.utils.PageResult;
import org.springframework.data.domain.Pageable;
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.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* @author Chanheng
* @date 2025-05-26
*/
@RestController
@RequiredArgsConstructor
@Api(tags = "Wait List Management")
@RequestMapping("/api/wait-list")
public class WaitListController {
private final WaitListService waitListService;
private static final String ENTITY_NAME = "waitList";
@ApiOperation("Export wait list data")
@GetMapping(value = "/download")
@PreAuthorize("@el.check('waitList:list')")
public void exportWaitList(HttpServletResponse response, WaitListQueryCriteria criteria) throws IOException {
waitListService.download(waitListService.queryAll(criteria), response);
}
@ApiOperation("Query wait list entries")
@GetMapping
@PreAuthorize("@el.check('waitList:list')")
public ResponseEntity<PageResult<WaitListDto>> queryWaitList(WaitListQueryCriteria criteria, Pageable pageable) {
return new ResponseEntity<>(waitListService.queryAll(criteria, pageable), HttpStatus.OK);
}
@ApiOperation("Query wait list entries by event ID")
@GetMapping(value = "/event/{eventId}")
@PreAuthorize("@el.check('waitList:list')")
public ResponseEntity<List<WaitListDto>> queryWaitListByEvent(@PathVariable Long eventId) {
return new ResponseEntity<>(waitListService.findByEventId(eventId), HttpStatus.OK);
}
@ApiOperation("Query wait list entries by player ID")
@GetMapping(value = "/player/{playerId}")
@PreAuthorize("@el.check('waitList:list')")
public ResponseEntity<List<WaitListDto>> queryWaitListByPlayer(@PathVariable Long playerId) {
return new ResponseEntity<>(waitListService.findByPlayerId(playerId), HttpStatus.OK);
}
@ApiOperation("Add to wait list")
@PostMapping
@PreAuthorize("@el.check('waitList:add')")
public ResponseEntity<WaitListDto> createWaitList(@Validated @RequestBody WaitList resources) {
return new ResponseEntity<>(waitListService.create(resources), HttpStatus.CREATED);
}
@ApiOperation("Update wait list entry")
@PutMapping
@PreAuthorize("@el.check('waitList:edit')")
public ResponseEntity<Object> updateWaitList(@Validated @RequestBody WaitList resources) {
waitListService.update(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@ApiOperation("Promote player from wait list to participant")
@PutMapping(value = "/{id}/promote")
@PreAuthorize("@el.check('waitList:edit')")
public ResponseEntity<Object> promoteWaitListEntry(@PathVariable Long id) {
boolean success = waitListService.promoteToParticipant(id);
return new ResponseEntity<>(success ? HttpStatus.OK : HttpStatus.BAD_REQUEST);
}
@ApiOperation("Delete wait list entry")
@DeleteMapping(value = "/{id}")
@PreAuthorize("@el.check('waitList:del')")
public ResponseEntity<Object> deleteWaitList(@PathVariable Long id) {
waitListService.delete(id);
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -18,6 +18,7 @@ package com.srr.service;
import com.srr.domain.Event; 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.enumeration.EventStatus; import com.srr.enumeration.EventStatus;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@ -70,6 +71,13 @@ public interface EventService {
EventDto updateStatus(Long id, EventStatus status); EventDto updateStatus(Long id, EventStatus status);
/**
* Join an event
* @param joinEventDto Data for joining an event
* @return Updated event data
*/
EventDto joinEvent(JoinEventDto joinEventDto);
/** /**
* Multi-select delete * Multi-select delete
* @param ids / * @param ids /

View File

@ -0,0 +1,118 @@
/*
* 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.WaitList;
import com.srr.dto.WaitListDto;
import com.srr.dto.WaitListQueryCriteria;
import me.zhengjie.utils.PageResult;
import org.springframework.data.domain.Pageable;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* @author Chanheng
* @date 2025-05-26
*/
public interface WaitListService {
/**
* Add a player to the wait list
* @param waitList Player wait list entry
* @return Created wait list entry
*/
WaitListDto create(WaitList waitList);
/**
* Update wait list entry
* @param waitList Updated wait list entry
*/
void update(WaitList waitList);
/**
* Delete wait list entry
* @param id Wait list entry ID
*/
void delete(Long id);
/**
* Delete multiple wait list entries
* @param ids List of wait list entry IDs
*/
void deleteAll(List<Long> ids);
/**
* Find a wait list entry by ID
* @param id Wait list entry ID
* @return Wait list entry
*/
WaitListDto findById(Long id);
/**
* Find wait list entries by event ID
* @param eventId Event ID
* @return List of wait list entries
*/
List<WaitListDto> findByEventId(Long eventId);
/**
* Find wait list entries by player ID
* @param playerId Player ID
* @return List of wait list entries
*/
List<WaitListDto> findByPlayerId(Long playerId);
/**
* Find wait list entry for a specific player and event
* @param eventId Event ID
* @param playerId Player ID
* @return Wait list entry
*/
WaitListDto findByEventAndPlayer(Long eventId, Long playerId);
/**
* Promote a player from wait list to event participant
* @param waitListId Wait list entry ID
* @return True if promotion successful
*/
boolean promoteToParticipant(Long waitListId);
/**
* Query wait list with criteria
* @param criteria Query criteria
* @param pageable Pagination information
* @return Page result with wait list entries
*/
PageResult<WaitListDto> queryAll(WaitListQueryCriteria criteria, Pageable pageable);
/**
* Query all wait list entries
* @param criteria Query criteria
* @return List of wait list entries
*/
List<WaitListDto> queryAll(WaitListQueryCriteria criteria);
/**
* Export wait list data
* @param queryAll All wait list entries
* @param response HTTP response
* @throws IOException If export fails
*/
void download(List<WaitListDto> queryAll, HttpServletResponse response) throws IOException;
}

View File

@ -26,6 +26,10 @@ import com.srr.service.EventService;
import com.srr.dto.EventDto; import com.srr.dto.EventDto;
import com.srr.dto.EventQueryCriteria; import com.srr.dto.EventQueryCriteria;
import com.srr.dto.mapstruct.EventMapper; import com.srr.dto.mapstruct.EventMapper;
import com.srr.dto.JoinEventDto;
import com.srr.repository.TeamPlayerRepository;
import com.srr.repository.TeamRepository;
import me.zhengjie.exception.BadRequestException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@ -56,6 +60,8 @@ public class EventServiceImpl implements EventService {
private final EventRepository eventRepository; private final EventRepository eventRepository;
private final EventMapper eventMapper; private final EventMapper eventMapper;
private final TeamRepository teamRepository;
private final TeamPlayerRepository teamPlayerRepository;
@Override @Override
public PageResult<EventDto> queryAll(EventQueryCriteria criteria, Pageable pageable) { public PageResult<EventDto> queryAll(EventQueryCriteria criteria, Pageable pageable) {
@ -110,6 +116,50 @@ public class EventServiceImpl implements EventService {
return eventMapper.toDto(result); return eventMapper.toDto(result);
} }
@Override
@Transactional(rollbackFor = Exception.class)
public EventDto joinEvent(JoinEventDto joinEventDto) {
// Find the event
Event event = eventRepository.findById(joinEventDto.getEventId())
.orElseThrow(() -> new EntityNotFoundException(Event.class, "id", joinEventDto.getEventId()));
// Check if event allows joining
if (event.getStatus() != EventStatus.OPEN) {
throw new BadRequestException("Event is not open for joining");
}
// Check if event is full and handle waitlist
boolean isWaitList = joinEventDto.getJoinWaitList() != null && joinEventDto.getJoinWaitList();
if (event.getMaxParticipants() != null &&
event.getCurrentParticipants() >= event.getMaxParticipants() &&
!isWaitList) {
if (!event.isAllowWaitList()) {
throw new BadRequestException("Event is full and does not allow waitlist");
}
// Set joinWaitList to true if event is full and waitlist is allowed
isWaitList = true;
}
// Handle team-related logic
if (joinEventDto.getTeamId() != null) {
// Add player to existing team
// For implementation, you'd use teamRepository and teamPlayerRepository
// to check if team exists and add the player
} else {
// Create new team for the player if needed
// or add as individual participant depending on event format
}
// Update participant count if not joining waitlist
if (!isWaitList) {
event.setCurrentParticipants(event.getCurrentParticipants() + 1);
}
// Save and return updated event
final var result = eventRepository.save(event);
return eventMapper.toDto(result);
}
@Override @Override
public void deleteAll(Long[] ids) { public void deleteAll(Long[] ids) {
for (Long id : ids) { for (Long id : ids) {
@ -125,7 +175,6 @@ public class EventServiceImpl implements EventService {
map.put("名称", event.getName()); map.put("名称", event.getName());
map.put("描述", event.getDescription()); map.put("描述", event.getDescription());
map.put("SINGLE, DOUBLE", event.getFormat()); map.put("SINGLE, DOUBLE", event.getFormat());
map.put("最大人数", event.getMaxPlayer());
map.put("位置", event.getLocation()); map.put("位置", event.getLocation());
map.put("图片", event.getImage()); map.put("图片", event.getImage());
map.put("创建时间", event.getCreateTime()); map.put("创建时间", event.getCreateTime());

View File

@ -0,0 +1,206 @@
/*
* 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.WaitList;
import com.srr.enumeration.WaitListStatus;
import com.srr.dto.WaitListDto;
import com.srr.dto.WaitListQueryCriteria;
import com.srr.repository.EventRepository;
import com.srr.repository.WaitListRepository;
import com.srr.service.WaitListService;
import lombok.RequiredArgsConstructor;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.exception.EntityNotFoundException;
import me.zhengjie.utils.FileUtil;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QueryHelp;
import me.zhengjie.utils.PageResult;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author Chanheng
* @date 2025-05-26
*/
@Service
@RequiredArgsConstructor
public class WaitListServiceImpl implements WaitListService {
private final WaitListRepository waitListRepository;
private final EventRepository eventRepository;
@Override
@Transactional
public WaitListDto create(WaitList resources) {
// Validate event exists
Event event = eventRepository.findById(resources.getEventId())
.orElseThrow(() -> new EntityNotFoundException(Event.class, "id", resources.getEventId()));
// Check if player is already in wait list
if (waitListRepository.findByEventIdAndPlayerId(resources.getEventId(), resources.getPlayerId()) != null) {
throw new BadRequestException("Player is already in wait list");
}
// Set default status
resources.setStatus(WaitListStatus.WAITING);
return mapToDto(waitListRepository.save(resources));
}
@Override
@Transactional
public void update(WaitList resources) {
WaitList waitList = waitListRepository.findById(resources.getId())
.orElseThrow(() -> new EntityNotFoundException(WaitList.class, "id", resources.getId()));
waitList.copy(resources);
waitListRepository.save(waitList);
}
@Override
@Transactional
public void delete(Long id) {
waitListRepository.deleteById(id);
}
@Override
@Transactional
public void deleteAll(List<Long> ids) {
waitListRepository.deleteAllById(ids);
}
@Override
public WaitListDto findById(Long id) {
WaitList waitList = waitListRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(WaitList.class, "id", id));
return mapToDto(waitList);
}
@Override
public List<WaitListDto> findByEventId(Long eventId) {
// Validate event exists
if (!eventRepository.existsById(eventId)) {
throw new EntityNotFoundException(Event.class, "id", eventId);
}
return waitListRepository.findByEventId(eventId).stream()
.map(this::mapToDto)
.collect(Collectors.toList());
}
@Override
public List<WaitListDto> findByPlayerId(Long playerId) {
return waitListRepository.findByPlayerId(playerId).stream()
.map(this::mapToDto)
.collect(Collectors.toList());
}
@Override
public WaitListDto findByEventAndPlayer(Long eventId, Long playerId) {
WaitList waitList = waitListRepository.findByEventIdAndPlayerId(eventId, playerId);
return waitList != null ? mapToDto(waitList) : null;
}
@Override
@Transactional
public boolean promoteToParticipant(Long waitListId) {
// Find wait list entry
WaitList waitList = waitListRepository.findById(waitListId)
.orElseThrow(() -> new EntityNotFoundException(WaitList.class, "id", waitListId));
// Find event
Event event = eventRepository.findById(waitList.getEventId())
.orElseThrow(() -> new EntityNotFoundException(Event.class, "id", waitList.getEventId()));
// Check if event is full
if (event.getCurrentParticipants() >= event.getMaxParticipants()) {
return false;
}
// Update wait list status
waitList.setStatus(WaitListStatus.PROMOTED);
waitListRepository.save(waitList);
// Increment participant count
event.setCurrentParticipants(event.getCurrentParticipants() + 1);
eventRepository.save(event);
// TODO: Add player to event participants (implementation depends on your data model)
return true;
}
@Override
public PageResult<WaitListDto> queryAll(WaitListQueryCriteria criteria, Pageable pageable) {
Page<WaitList> page = waitListRepository.findAll((root, criteriaQuery, criteriaBuilder) ->
QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
return PageUtil.toPage(page.map(this::mapToDto));
}
@Override
public List<WaitListDto> queryAll(WaitListQueryCriteria criteria) {
return waitListRepository.findAll((root, criteriaQuery, criteriaBuilder) ->
QueryHelp.getPredicate(root, criteria, criteriaBuilder))
.stream()
.map(this::mapToDto)
.collect(Collectors.toList());
}
@Override
public void download(List<WaitListDto> queryAll, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (WaitListDto waitList : queryAll) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Event ID", waitList.getEventId());
map.put("Player ID", waitList.getPlayerId());
map.put("Status", waitList.getStatus() != null ? waitList.getStatus().getDescription() : null);
map.put("Notes", waitList.getNotes());
map.put("Creation Time", waitList.getCreateTime());
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
/**
* Map entity to DTO
*/
private WaitListDto mapToDto(WaitList waitList) {
if (waitList == null) {
return null;
}
WaitListDto dto = new WaitListDto();
dto.setId(waitList.getId());
dto.setEventId(waitList.getEventId());
dto.setPlayerId(waitList.getPlayerId());
dto.setNotes(waitList.getNotes());
dto.setStatus(waitList.getStatus());
dto.setCreateTime(waitList.getCreateTime());
dto.setUpdateTime(waitList.getUpdateTime());
// Load relationships if needed (can be implemented based on requirements)
return dto;
}
}