Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package DGU_AI_LAB.admin_be.domain.alarm.service;

import DGU_AI_LAB.admin_be.domain.requests.entity.Request;
import DGU_AI_LAB.admin_be.domain.users.entity.User;
import DGU_AI_LAB.admin_be.error.ErrorCode;
import DGU_AI_LAB.admin_be.error.exception.BusinessException;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -213,6 +214,38 @@ public void sendNewRequestNotification(Request request) {
sendSlackAlert(message, targetWebhookUrl);
}

/**
* 사용자에게 서버 사용 신청 승인 알림을 보냅니다.
* @param request 승인된 Request 엔티티
*/
public void sendApprovalNotification(Request request) {
User user = request.getUser();
String subject = "[DGU AI LAB] 서버 사용 신청이 승인되었습니다.";
String message = String.format(
"""
🎉 %s님의 서버 사용 신청이 성공적으로 승인되었습니다! 🎉

아래 정보를 사용하여 서버에 접속해 주세요.
-------------------------------------
- Ubuntu 사용자 이름: %s
- 할당된 서버: %s
- 컨테이너 이미지: %s:%s
- 할당된 볼륨 크기: %d GiB
- 만료일: %s
-------------------------------------

궁금한 점이 있다면 관리자에게 문의해 주세요.
""",
user.getName(),
request.getUbuntuUsername(),
request.getResourceGroup().getServerName(),
request.getContainerImage().getImageName(),
request.getContainerImage().getImageVersion(),
request.getVolumeSizeGiB(),
request.getExpiresAt().toLocalDate().toString()
);

sendAllAlerts(user.getName(), user.getEmail(), subject, message);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
@Table(name = "gpus")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "gpuId")
public class Gpu {

@Id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@Table(name = "`groups`")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "groupId")
@EqualsAndHashCode(of = "ubuntuGid")
public class Group {

@Id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ public ResponseEntity<SuccessResponse<?>> getChangeRequests() {
return SuccessResponse.ok(changeRequests);
}

/**
* 모든 변경 요청 목록 조회 (관리자용)
* 모든 상태의 ChangeRequest 목록을 반환합니다.
*/
@GetMapping("/all")
public ResponseEntity<SuccessResponse<?>> getAllChangeRequests() {
List<ChangeRequestResponseDTO> changeRequests = adminRequestQueryService.getAllChangeRequests();
return SuccessResponse.ok(changeRequests);
}

@PatchMapping("/approve")
public ResponseEntity<SuccessResponse<?>> approveModification(
@AuthenticationPrincipal(expression = "userId") Long adminId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import DGU_AI_LAB.admin_be.domain.requests.controller.docs.RequestApi;
import DGU_AI_LAB.admin_be.domain.requests.dto.request.SingleChangeRequestDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.request.SaveRequestRequestDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.ChangeRequestResponseDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.SaveRequestResponseDTO;
import DGU_AI_LAB.admin_be.domain.requests.service.RequestCommandService;
import DGU_AI_LAB.admin_be.domain.requests.service.RequestQueryService;
Expand Down Expand Up @@ -74,4 +75,13 @@ public ResponseEntity<SuccessResponse<?>> getAllFulfilledUsernames() {
List<String> usernames = requestQueryService.getAllFulfilledUsernames();
return SuccessResponse.ok(usernames);
}

/**
* 나의 변경 요청 목록 조회
*/
@GetMapping("/my/changes")
public ResponseEntity<SuccessResponse<?>> getMyChangeRequests(@AuthenticationPrincipal CustomUserDetails user) {
List<ChangeRequestResponseDTO> changeRequests = requestQueryService.getMyChangeRequests(user.getUserId());
return SuccessResponse.ok(changeRequests);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public interface AdminRequestChangeApi {
})
ResponseEntity<SuccessResponse<?>> getChangeRequests();

@Operation(summary = "모든 변경 요청 목록 조회", description = "모든 상태의 변경 요청 목록을 조회합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공",
content = @Content(schema = @Schema(implementation = SuccessResponse.class)))
})
ResponseEntity<SuccessResponse<?>> getAllChangeRequests();

@Operation(summary = "변경 요청 승인", description = "PENDING 상태의 변경 요청을 승인하고 설정을 업데이트합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package DGU_AI_LAB.admin_be.domain.requests.controller.docs;

import DGU_AI_LAB.admin_be.domain.requests.dto.request.SaveRequestRequestDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.ChangeRequestResponseDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.SaveRequestResponseDTO;
import DGU_AI_LAB.admin_be.global.auth.CustomUserDetails;
import DGU_AI_LAB.admin_be.global.common.SuccessResponse;
Expand Down Expand Up @@ -78,4 +79,18 @@ ResponseEntity<SuccessResponse<?>> getMyApprovedRequests(
content = @Content(schema = @Schema(implementation = SuccessResponse.class))
)
ResponseEntity<SuccessResponse<?>> getAllFulfilledUsernames();

@Operation(
summary = "내 변경 요청 목록 조회",
description = "로그인된 사용자가 제출한 모든 변경 요청 내역을 조회합니다. 모든 상태(PENDING, FULFILLED, DENIED)의 변경 요청이 포함됩니다."
)
@ApiResponse(
responseCode = "200",
description = "조회 성공",
content = @Content(array = @ArraySchema(schema = @Schema(implementation = ChangeRequestResponseDTO.class)))
)
ResponseEntity<SuccessResponse<?>> getMyChangeRequests(
@Parameter(hidden = true, description = "인증된 사용자")
CustomUserDetails user
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public record ChangeRequestResponseDTO(
@JsonRawValue String newValue,
String reason,
Status status,
String adminComment,
AdminUserInfo requestedBy,
LocalDateTime createdAt
) {
Expand All @@ -30,6 +31,7 @@ public static ChangeRequestResponseDTO fromEntity(ChangeRequest changeRequest) {
.newValue(changeRequest.getNewValue())
.reason(changeRequest.getReason())
.status(changeRequest.getStatus())
.adminComment(changeRequest.getAdminComment())
.requestedBy(AdminUserInfo.fromEntity(changeRequest.getRequestedBy()))
.createdAt(changeRequest.getCreatedAt())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@Table(name = "requests")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "ubuntuUsername", callSuper = false)
public class Request extends BaseTimeEntity {

@Id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@

@Entity
@Table(name = "request_groups")
@Access(AccessType.FIELD)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "id")
@EqualsAndHashCode(of = {"request", "group"})
public class RequestGroup {

@EmbeddedId
private RequestGroupId id;

@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;

@ManyToOne(fetch = FetchType.LAZY) @MapsId("requestId")
@JoinColumn(name = "request_id", nullable = false)
private Request request;
Expand All @@ -28,6 +24,9 @@ public class RequestGroup {
@JoinColumn(name = "ubuntu_gid", nullable = false)
private Group group;

@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;

@Builder
public RequestGroup(Request request, Group group) {
this.request = request;
Expand All @@ -36,10 +35,12 @@ public RequestGroup(Request request, Group group) {

@PrePersist
void onCreate() {
if (createdAt == null) createdAt = LocalDateTime.now();
// @MapsId가 id를 자동으로 채워주지만 방어적으로 ID도 보완
if (id == null && request != null && group != null) {
id = new RequestGroupId(request.getRequestId(), group.getUbuntuGid());
if (this.createdAt == null) {
this.createdAt = LocalDateTime.now();
}

if (this.id == null) {
this.id = new RequestGroupId(this.request.getRequestId(), this.group.getUbuntuGid());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
public interface ChangeRequestRepository extends JpaRepository<ChangeRequest, Long> {
List<ChangeRequest> findAllByStatus(Status status);
List<ChangeRequest> findAllByRequestedBy_UserIdAndStatus(Long userId, Status status);
List<ChangeRequest> findAllByRequestedBy_UserId(Long userId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package DGU_AI_LAB.admin_be.domain.requests.service;

import DGU_AI_LAB.admin_be.domain.alarm.service.AlarmService;
import DGU_AI_LAB.admin_be.domain.containerImage.entity.ContainerImage;
import DGU_AI_LAB.admin_be.domain.containerImage.repository.ContainerImageRepository;
import DGU_AI_LAB.admin_be.domain.groups.entity.Group;
Expand Down Expand Up @@ -42,6 +43,8 @@
@Transactional
public class AdminRequestCommandService {

private final AlarmService alarmService;

private final RequestRepository requestRepository;
private final UserRepository userRepository;
private final ContainerImageRepository containerImageRepository;
Expand Down Expand Up @@ -152,8 +155,16 @@ public SaveRequestResponseDTO approveRequest(ApproveRequestDTO dto) {
ResourceGroup rg = resourceGroupRepository.findById(dto.resourceGroupId())
.orElseThrow(() -> new BusinessException(ErrorCode.RESOURCE_NOT_FOUND));
request.approve(image, rg, dto.volumeSizeGiB(), dto.adminComment());
requestRepository.flush();
// requestRepository.flush();

// 4. 사용자에게 승인 알림 발송
try {
alarmService.sendApprovalNotification(request);
log.info("사용자 '{}'에게 승인 알림을 성공적으로 발송했습니다.", request.getUser().getName());
} catch (Exception e) {
log.warn("사용자 '{}'에게 승인 알림을 보내는 데 실패했습니다. (RequestId: {})",
request.getUser().getName(), request.getRequestId(), e);
}
return SaveRequestResponseDTO.fromEntity(request);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,10 @@ public List<ChangeRequestResponseDTO> getChangeRequests() {
.map(ChangeRequestResponseDTO::fromEntity)
.collect(Collectors.toList());
}

public List<ChangeRequestResponseDTO> getAllChangeRequests() {
return changeRequestRepository.findAll().stream()
.map(ChangeRequestResponseDTO::fromEntity)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package DGU_AI_LAB.admin_be.domain.requests.service;

import DGU_AI_LAB.admin_be.domain.portRequests.service.PortRequestService;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.ChangeRequestResponseDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.ContainerInfoDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.PortMappingDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.ResourceUsageDTO;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.SaveRequestResponseDTO;
import DGU_AI_LAB.admin_be.domain.requests.entity.Request;
import DGU_AI_LAB.admin_be.domain.requests.entity.Status;
import DGU_AI_LAB.admin_be.domain.requests.repository.ChangeRequestRepository;
import DGU_AI_LAB.admin_be.domain.requests.repository.RequestRepository;
import DGU_AI_LAB.admin_be.domain.users.repository.UserRepository;
import DGU_AI_LAB.admin_be.error.ErrorCode;
Expand All @@ -25,6 +27,7 @@
public class RequestQueryService {

private final RequestRepository requestRepository;
private final ChangeRequestRepository changeRequestRepository;
private final UserRepository userRepository;
private final PortRequestService portRequestService;

Expand Down Expand Up @@ -79,4 +82,14 @@ public List<SaveRequestResponseDTO> getApprovedRequestsByUserId(Long userId) {
.toList();
}

/** 내 변경 요청 목록 조회 */
public List<ChangeRequestResponseDTO> getMyChangeRequests(Long userId) {
if (!userRepository.existsById(userId)) {
throw new BusinessException(ErrorCode.USER_NOT_FOUND);
}
return changeRequestRepository.findAllByRequestedBy_UserId(userId).stream()
.map(ChangeRequestResponseDTO::fromEntity)
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@Table(name = "users")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "email", callSuper = false)
public class User extends BaseTimeEntity {

@Id
Expand Down