From 81c8c2cf4e1acbe75de00aab7c1270b6a84f7732 Mon Sep 17 00:00:00 2001 From: chanhengseang Date: Mon, 26 May 2025 11:59:22 -0700 Subject: [PATCH] add waitlist --- .../db/migration/V11__add_event_tags.sql | 6 +- .../db/migration/V13__create_wait_list.sql | 18 ++ .../main/resources/db/migration/V2__event.sql | 1 - sport/src/main/java/com/srr/domain/Event.java | 12 +- .../main/java/com/srr/domain/WaitList.java | 85 ++++++++ sport/src/main/java/com/srr/dto/EventDto.java | 9 +- .../main/java/com/srr/dto/JoinEventDto.java | 32 ++- .../main/java/com/srr/dto/WaitListDto.java | 65 ++++++ .../com/srr/dto/WaitListQueryCriteria.java | 51 +++++ .../com/srr/enumeration/WaitListStatus.java | 36 +++ .../srr/repository/WaitListRepository.java | 66 ++++++ .../java/com/srr/rest/EventController.java | 6 + .../java/com/srr/rest/WaitListController.java | 108 +++++++++ .../java/com/srr/service/EventService.java | 8 + .../java/com/srr/service/WaitListService.java | 118 ++++++++++ .../srr/service/impl/EventServiceImpl.java | 51 ++++- .../srr/service/impl/WaitListServiceImpl.java | 206 ++++++++++++++++++ 17 files changed, 866 insertions(+), 12 deletions(-) create mode 100644 eladmin-system/src/main/resources/db/migration/V13__create_wait_list.sql create mode 100644 sport/src/main/java/com/srr/domain/WaitList.java create mode 100644 sport/src/main/java/com/srr/dto/WaitListDto.java create mode 100644 sport/src/main/java/com/srr/dto/WaitListQueryCriteria.java create mode 100644 sport/src/main/java/com/srr/enumeration/WaitListStatus.java create mode 100644 sport/src/main/java/com/srr/repository/WaitListRepository.java create mode 100644 sport/src/main/java/com/srr/rest/WaitListController.java create mode 100644 sport/src/main/java/com/srr/service/WaitListService.java create mode 100644 sport/src/main/java/com/srr/service/impl/WaitListServiceImpl.java diff --git a/eladmin-system/src/main/resources/db/migration/V11__add_event_tags.sql b/eladmin-system/src/main/resources/db/migration/V11__add_event_tags.sql index 3609626a..0321b10b 100644 --- a/eladmin-system/src/main/resources/db/migration/V11__add_event_tags.sql +++ b/eladmin-system/src/main/resources/db/migration/V11__add_event_tags.sql @@ -1,4 +1,6 @@ -- Add tags column to event table ALTER TABLE event - add column poster_image varchar(255), - ADD COLUMN tags VARCHAR(255) NULL COMMENT 'Tags stored as comma-delimited string'; + ADD COLUMN poster_image VARCHAR(255), + 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'; diff --git a/eladmin-system/src/main/resources/db/migration/V13__create_wait_list.sql b/eladmin-system/src/main/resources/db/migration/V13__create_wait_list.sql new file mode 100644 index 00000000..e06f0c77 --- /dev/null +++ b/eladmin-system/src/main/resources/db/migration/V13__create_wait_list.sql @@ -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); diff --git a/eladmin-system/src/main/resources/db/migration/V2__event.sql b/eladmin-system/src/main/resources/db/migration/V2__event.sql index 85a130d0..189df703 100644 --- a/eladmin-system/src/main/resources/db/migration/V2__event.sql +++ b/eladmin-system/src/main/resources/db/migration/V2__event.sql @@ -41,7 +41,6 @@ create table event name varchar(32) not null comment '名称', description varchar(255) null comment '描述', format enum ('SINGLE', 'DOUBLE', 'TEAM') not null, - max_player int null comment '最大人数', location varchar(255) null comment '位置', image varchar(255) null comment '图片', create_time datetime null comment '创建时间', diff --git a/sport/src/main/java/com/srr/domain/Event.java b/sport/src/main/java/com/srr/domain/Event.java index facfa69d..51fd4113 100644 --- a/sport/src/main/java/com/srr/domain/Event.java +++ b/sport/src/main/java/com/srr/domain/Event.java @@ -68,10 +68,6 @@ public class Event implements Serializable { @ApiModelProperty(value = "SINGLE, DOUBLE") private Format format; - @Column(name = "`max_player`") - @ApiModelProperty(value = "Maximum number of people") - private Integer maxPlayer; - @Column(name = "`location`") @ApiModelProperty(value = "Location") private String location; @@ -140,6 +136,14 @@ public class Event implements Serializable { @Column(name = "`allow_wait_list`") 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`") private String posterImage; diff --git a/sport/src/main/java/com/srr/domain/WaitList.java b/sport/src/main/java/com/srr/domain/WaitList.java new file mode 100644 index 00000000..df114701 --- /dev/null +++ b/sport/src/main/java/com/srr/domain/WaitList.java @@ -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(); + } +} diff --git a/sport/src/main/java/com/srr/dto/EventDto.java b/sport/src/main/java/com/srr/dto/EventDto.java index b0146795..3b5b1b7f 100644 --- a/sport/src/main/java/com/srr/dto/EventDto.java +++ b/sport/src/main/java/com/srr/dto/EventDto.java @@ -45,9 +45,6 @@ public class EventDto implements Serializable { @ApiModelProperty(value = "SINGLE, DOUBLE") private Format format; - @ApiModelProperty(value = "最大人数") - private Integer maxPlayer; - @ApiModelProperty(value = "位置") private String location; @@ -95,6 +92,12 @@ public class EventDto implements Serializable { 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") private List coHostPlayers; diff --git a/sport/src/main/java/com/srr/dto/JoinEventDto.java b/sport/src/main/java/com/srr/dto/JoinEventDto.java index 6090db72..6f9e2675 100644 --- a/sport/src/main/java/com/srr/dto/JoinEventDto.java +++ b/sport/src/main/java/com/srr/dto/JoinEventDto.java @@ -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; +import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotNull; +/** + * @author Chanheng + * @date 2025-05-26 + */ @Data public class JoinEventDto { - private Long playerId; + + @ApiModelProperty(value = "Event ID") @NotNull private Long eventId; + + @ApiModelProperty(value = "Player ID") + @NotNull + private Long playerId; + + @ApiModelProperty(value = "Team ID (optional)") private Long teamId; + + @ApiModelProperty(value = "Join as wait list") + private Boolean joinWaitList = false; } diff --git a/sport/src/main/java/com/srr/dto/WaitListDto.java b/sport/src/main/java/com/srr/dto/WaitListDto.java new file mode 100644 index 00000000..240d18d9 --- /dev/null +++ b/sport/src/main/java/com/srr/dto/WaitListDto.java @@ -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; +} diff --git a/sport/src/main/java/com/srr/dto/WaitListQueryCriteria.java b/sport/src/main/java/com/srr/dto/WaitListQueryCriteria.java new file mode 100644 index 00000000..c16949ab --- /dev/null +++ b/sport/src/main/java/com/srr/dto/WaitListQueryCriteria.java @@ -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 createTime; +} diff --git a/sport/src/main/java/com/srr/enumeration/WaitListStatus.java b/sport/src/main/java/com/srr/enumeration/WaitListStatus.java new file mode 100644 index 00000000..c87b79b9 --- /dev/null +++ b/sport/src/main/java/com/srr/enumeration/WaitListStatus.java @@ -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; +} diff --git a/sport/src/main/java/com/srr/repository/WaitListRepository.java b/sport/src/main/java/com/srr/repository/WaitListRepository.java new file mode 100644 index 00000000..07f26f70 --- /dev/null +++ b/sport/src/main/java/com/srr/repository/WaitListRepository.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.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, JpaSpecificationExecutor { + + /** + * Find all wait list entries for an event + * @param eventId The event ID + * @return List of wait list entries + */ + List findByEventId(Long eventId); + + /** + * Find all wait list entries for a player + * @param playerId The player ID + * @return List of wait list entries + */ + List 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 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); +} diff --git a/sport/src/main/java/com/srr/rest/EventController.java b/sport/src/main/java/com/srr/rest/EventController.java index b2e36ebf..695509f4 100644 --- a/sport/src/main/java/com/srr/rest/EventController.java +++ b/sport/src/main/java/com/srr/rest/EventController.java @@ -93,7 +93,13 @@ public class EventController { return new ResponseEntity<>(result, HttpStatus.OK); } + @PostMapping("/{id}/join") + @Log("Join event") + @ApiOperation("Join event") + @PreAuthorize("@el.check('event:join')") public ResponseEntity joinEvent(@PathVariable Long id, @RequestBody JoinEventDto joinEventDto) { + // Ensure ID in path matches ID in DTO + joinEventDto.setEventId(id); final EventDto result = eventService.joinEvent(joinEventDto); return new ResponseEntity<>(result, HttpStatus.OK); } diff --git a/sport/src/main/java/com/srr/rest/WaitListController.java b/sport/src/main/java/com/srr/rest/WaitListController.java new file mode 100644 index 00000000..7b9c33c7 --- /dev/null +++ b/sport/src/main/java/com/srr/rest/WaitListController.java @@ -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> 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> 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> 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 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 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 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 deleteWaitList(@PathVariable Long id) { + waitListService.delete(id); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/sport/src/main/java/com/srr/service/EventService.java b/sport/src/main/java/com/srr/service/EventService.java index ff0e5e08..b11397c3 100644 --- a/sport/src/main/java/com/srr/service/EventService.java +++ b/sport/src/main/java/com/srr/service/EventService.java @@ -18,6 +18,7 @@ package com.srr.service; import com.srr.domain.Event; import com.srr.dto.EventDto; import com.srr.dto.EventQueryCriteria; +import com.srr.dto.JoinEventDto; import com.srr.enumeration.EventStatus; import org.springframework.data.domain.Pageable; @@ -70,6 +71,13 @@ public interface EventService { 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 * @param ids / diff --git a/sport/src/main/java/com/srr/service/WaitListService.java b/sport/src/main/java/com/srr/service/WaitListService.java new file mode 100644 index 00000000..26ee9b25 --- /dev/null +++ b/sport/src/main/java/com/srr/service/WaitListService.java @@ -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 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 findByEventId(Long eventId); + + /** + * Find wait list entries by player ID + * @param playerId Player ID + * @return List of wait list entries + */ + List 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 queryAll(WaitListQueryCriteria criteria, Pageable pageable); + + /** + * Query all wait list entries + * @param criteria Query criteria + * @return List of wait list entries + */ + List 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 queryAll, HttpServletResponse response) throws IOException; +} diff --git a/sport/src/main/java/com/srr/service/impl/EventServiceImpl.java b/sport/src/main/java/com/srr/service/impl/EventServiceImpl.java index 1e94248d..9473c08b 100644 --- a/sport/src/main/java/com/srr/service/impl/EventServiceImpl.java +++ b/sport/src/main/java/com/srr/service/impl/EventServiceImpl.java @@ -26,6 +26,10 @@ import com.srr.service.EventService; import com.srr.dto.EventDto; import com.srr.dto.EventQueryCriteria; 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.transaction.annotation.Transactional; import org.springframework.data.domain.Page; @@ -56,6 +60,8 @@ public class EventServiceImpl implements EventService { private final EventRepository eventRepository; private final EventMapper eventMapper; + private final TeamRepository teamRepository; + private final TeamPlayerRepository teamPlayerRepository; @Override public PageResult queryAll(EventQueryCriteria criteria, Pageable pageable) { @@ -110,6 +116,50 @@ public class EventServiceImpl implements EventService { 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 public void deleteAll(Long[] ids) { for (Long id : ids) { @@ -125,7 +175,6 @@ public class EventServiceImpl implements EventService { map.put("名称", event.getName()); map.put("描述", event.getDescription()); map.put("SINGLE, DOUBLE", event.getFormat()); - map.put("最大人数", event.getMaxPlayer()); map.put("位置", event.getLocation()); map.put("图片", event.getImage()); map.put("创建时间", event.getCreateTime()); diff --git a/sport/src/main/java/com/srr/service/impl/WaitListServiceImpl.java b/sport/src/main/java/com/srr/service/impl/WaitListServiceImpl.java new file mode 100644 index 00000000..14a1dd4d --- /dev/null +++ b/sport/src/main/java/com/srr/service/impl/WaitListServiceImpl.java @@ -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 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 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 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 queryAll(WaitListQueryCriteria criteria, Pageable pageable) { + Page page = waitListRepository.findAll((root, criteriaQuery, criteriaBuilder) -> + QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable); + return PageUtil.toPage(page.map(this::mapToDto)); + } + + @Override + public List 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 queryAll, HttpServletResponse response) throws IOException { + List> list = new ArrayList<>(); + for (WaitListDto waitList : queryAll) { + Map 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; + } +}