Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ee382d3
CLAP-150 Fix: 웹훅 및 이메일 관련 설정들 환경변수화
joowojr Jan 29, 2025
58d3427
CLAP-150 Fix: email config 추가
joowojr Jan 29, 2025
ed85887
CLAP-150 Remove: test yml 필요없는 코드 제거
joowojr Jan 29, 2025
dbf5a52
CLAP-155 Refactor: 초기 및 재계산 로직에서 쓰일 processor order 값 전역 상수 선언
joowojr Jan 29, 2025
ab56c5a
CLAP-155 Rename: 패키지 위치로 인한 수정
joowojr Jan 29, 2025
14ba998
CLAP-155 Feat: 작업보드에서의 task 순서 변경 request dto 추가
joowojr Jan 29, 2025
9a80afe
CLAP-155 Feat: 작업 보드에서의 task 순서 변경 usecase 구현
joowojr Jan 29, 2025
dd75af0
CLAP-155 Feat: 작업 보드에서의 task 순서 변경 usecase의 비즈니스로직 구현
joowojr Jan 29, 2025
0df5096
CLAP-155 Refactor: 작업보드에서 조회 가능한 상태들을 task status에서 도메인 비즈니스 로직으로 리팩토링
joowojr Jan 29, 2025
4cac681
CLAP-155 Feat: 작업 보드에서의 task 순서 변경 API 구현
joowojr Jan 29, 2025
bda652e
CLAP-155 Remove: 중복되는 메서드 통합
joowojr Jan 29, 2025
79cfa62
CLAP-155 Rename: 네이밍 수정
joowojr Jan 29, 2025
d0115c4
CLAP-155 Refactor: 작업 순서 변경 시, 상태값과 id를 통해 엔티티를 찾도록 수정
joowojr Jan 29, 2025
c9dc206
CLAP-155 Refactor: 순서를 변경할 작업과, 전/후의 작업의 상태를 비교 검증하는 메서드 삭제
joowojr Jan 29, 2025
b30c84b
CLAP-155 Docs: 주석 작성
joowojr Jan 29, 2025
0a62b2e
Merge branch 'develop' into CLAP-155
joowojr Jan 29, 2025
15ea89d
CLAP-155 Refactor: 승인권한이 있는 member을 바로 찾도록 리팩토링
joowojr Jan 29, 2025
7da6f9f
CLAP-155 Refactor: 요청자인이 검증하는 로직을 도메인 레벨로 이동
joowojr Jan 29, 2025
9605bff
CLAP-155 Refactor: 작업이 요청 상태인지 검증하는 로직 도메인 레이어로 이동
joowojr Jan 29, 2025
b175bdf
CLAP-155 Refactor: 승인권한이 있는 담당자를 조회하도록 수정
joowojr Jan 29, 2025
84636a3
CLAP-150 Fix: 웹훅 및 이메일 관련 설정들 환경변수화
joowojr Jan 29, 2025
a7e726e
CLAP-150 Fix: email config 추가
joowojr Jan 29, 2025
550fa84
CLAP-150 Remove: test yml 필요없는 코드 제거
joowojr Jan 29, 2025
20f0026
CLAP-155 Refactor: 초기 및 재계산 로직에서 쓰일 processor order 값 전역 상수 선언
joowojr Jan 29, 2025
67f654a
git rebase
joowojr Jan 29, 2025
913723a
CLAP-155 Feat: 작업보드에서의 task 순서 변경 request dto 추가
joowojr Jan 29, 2025
790107a
CLAP-155 Feat: 작업 보드에서의 task 순서 변경 usecase 구현
joowojr Jan 29, 2025
362b98f
CLAP-155 Feat: 작업 보드에서의 task 순서 변경 usecase의 비즈니스로직 구현
joowojr Jan 29, 2025
58e0367
CLAP-155 Refactor: 작업보드에서 조회 가능한 상태들을 task status에서 도메인 비즈니스 로직으로 리팩토링
joowojr Jan 29, 2025
13fc27d
CLAP-155 Feat: 작업 보드에서의 task 순서 변경 API 구현
joowojr Jan 29, 2025
1d22e46
CLAP-155 Remove: 중복되는 메서드 통합
joowojr Jan 29, 2025
76a980f
CLAP-155 Rename: 네이밍 수정
joowojr Jan 29, 2025
0d59aa7
CLAP-155 Refactor: 작업 순서 변경 시, 상태값과 id를 통해 엔티티를 찾도록 수정
joowojr Jan 29, 2025
33a8692
CLAP-155 Refactor: 순서를 변경할 작업과, 전/후의 작업의 상태를 비교 검증하는 메서드 삭제
joowojr Jan 29, 2025
cf3d27b
CLAP-155 Docs: 주석 작성
joowojr Jan 29, 2025
9579dfd
CLAP-155 Refactor: 승인권한이 있는 member을 바로 찾도록 리팩토링
joowojr Jan 29, 2025
43ad094
git rebase
joowojr Jan 29, 2025
555075d
CLAP-155 Refactor: 작업이 요청 상태인지 검증하는 로직 도메인 레이어로 이동
joowojr Jan 29, 2025
68f04b6
CLAP-155 Refactor: 승인권한이 있는 담당자를 조회하도록 수정
joowojr Jan 29, 2025
6499e85
merge conflict solve
joowojr Jan 29, 2025
851b12a
Merge remote-tracking branch 'origin/CLAP-155' into CLAP-155
joowojr Jan 29, 2025
fe9eb4d
merge conflict solve
joowojr Jan 29, 2025
98e216c
Merge branch 'develop' into CLAP-155
joowojr Jan 29, 2025
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
@@ -0,0 +1,14 @@
package clap.server.adapter.inbound.web.dto.task.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;

public record UpdateTaskOrderRequest(
@Schema(description = "변경할 위치의 상위 작업 ID, 가장 상위일 경우 0 입력")
long prevTaskId,
@Min(1) @Schema(description = "순서 또는 상태를 변경할 작업의 ID")
long targetTaskId,
@Schema(description = "변경할 위치의 하위 작업 ID, 가장 하위일 경우 0 입력")
long nextTaskId
) {
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package clap.server.adapter.inbound.web.task;

import clap.server.adapter.inbound.security.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.task.request.UpdateTaskOrderRequest;
import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.application.port.inbound.task.UpdateTaskBoardUsecase;
import clap.server.application.port.inbound.task.TaskBoardUsecase;
import clap.server.common.annotation.architecture.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;

Expand All @@ -26,16 +28,30 @@
@RequestMapping("/api/task-board")
public class TaskBoardController {
private final TaskBoardUsecase taskBoardUsecase;
private final UpdateTaskBoardUsecase updateTaskBoardUsecase;

@Operation(summary = "작업 보드 조회 API")
@GetMapping
public ResponseEntity<TaskBoardResponse> getTaskBoard(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int pageSize,
@Parameter(description = "yyyy-mm-dd 형식으로 입력합니다.") @RequestParam(required = false)
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate untilDate,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate untilDate,
@AuthenticationPrincipal SecurityUserDetails userInfo) {
Pageable pageable = PageRequest.of(page, pageSize);
return ResponseEntity.ok(taskBoardUsecase.getTaskBoards(userInfo.getUserId(), untilDate, pageable));
}

@Operation(summary = "작업 보드 순서 및 상태 변경 API")
@PatchMapping
public void updateTaskBoard(@Parameter(description = "전환될 작업의 상태, 상태 전환이 아니라면 입력 X", schema = @Schema(allowableValues = {"IN_PROGRESS", "PENDING_COMPLETED", "COMPLETED"}))
@RequestParam(required = false) TaskStatus status,
@RequestBody UpdateTaskOrderRequest request,
@AuthenticationPrincipal SecurityUserDetails userInfo) {
if (status == null) {
updateTaskBoardUsecase.updateTaskOrder(userInfo.getUserId(), request);
} else {
updateTaskBoardUsecase.updateTaskOrderAndStatus(userInfo.getUserId(), request, status);
}
}

}
10 changes: 5 additions & 5 deletions src/main/java/clap/server/adapter/outbound/api/AgitClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import clap.server.adapter.outbound.api.dto.SendAgitRequest;
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
import clap.server.application.port.outbound.webhook.SendAgitPort;
import clap.server.common.annotation.architecture.PersistenceAdapter;
import clap.server.common.annotation.architecture.ExternalApiAdapter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
Expand All @@ -12,12 +12,12 @@
import org.springframework.web.client.RestTemplate;


@PersistenceAdapter
@ExternalApiAdapter
@RequiredArgsConstructor
public class AgitClient implements SendAgitPort {

@Value("${agit.url}")
private String AGITWEBHOOK_URL;
@Value("${webhook.agit.url}")
private String AGIT_WEBHOOK_URL;

@Override
public void sendAgit(SendAgitRequest request) {
Expand Down Expand Up @@ -48,6 +48,6 @@ else if (request.notificationType() == NotificationType.PROCESSOR_CHANGED) {
HttpEntity<String> entity = new HttpEntity<>(payload, headers);

// Post 요청
restTemplate.exchange(AGITWEBHOOK_URL, HttpMethod.POST, entity, String.class);
restTemplate.exchange(AGIT_WEBHOOK_URL, HttpMethod.POST, entity, String.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import clap.server.adapter.outbound.api.dto.SendWebhookRequest;
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
import clap.server.application.port.outbound.webhook.SendEmailPort;
import clap.server.common.annotation.architecture.PersistenceAdapter;
import clap.server.common.annotation.architecture.ExternalApiAdapter;
import clap.server.exception.ApplicationException;
import clap.server.exception.code.NotificationErrorCode;
import jakarta.mail.internet.MimeMessage;
Expand All @@ -13,7 +13,7 @@
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;

@PersistenceAdapter
@ExternalApiAdapter
@RequiredArgsConstructor
public class EmailClient implements SendEmailPort {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import clap.server.adapter.outbound.api.dto.SendKakaoWorkRequest;
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
import clap.server.application.port.outbound.webhook.SendKaKaoWorkPort;
import clap.server.common.annotation.architecture.PersistenceAdapter;
import clap.server.common.annotation.architecture.ExternalApiAdapter;
import clap.server.exception.ApplicationException;
import clap.server.exception.code.NotificationErrorCode;
import lombok.RequiredArgsConstructor;
Expand All @@ -13,14 +13,14 @@
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RestTemplate;

@PersistenceAdapter
@ExternalApiAdapter
@RequiredArgsConstructor
public class KakaoWorkClient implements SendKaKaoWorkPort {

@Value("${kakaowork.url}")
@Value("${webhook.kakaowork.url}")
private String kakaworkUrl;

@Value("${kakaowork.auth}")
@Value("${webhook.kakaowork.auth}")
private String kakaworkAuth;

private final ObjectBlockService makeObjectBlock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import clap.server.application.port.outbound.s3.S3UploadPort;
import clap.server.common.annotation.architecture.InfrastructureAdapter;
import clap.server.config.s3.KakaoS3Config;
import clap.server.domain.model.task.FilePath;
import clap.server.common.constants.FilePathConstants;
import clap.server.exception.S3Exception;
import clap.server.exception.code.S3Errorcode;
import lombok.RequiredArgsConstructor;
Expand All @@ -26,11 +26,11 @@ public class S3UploadAdapter implements S3UploadPort {
private final KakaoS3Config kakaoS3Config;
private final S3Client s3Client;

public List<String> uploadFiles(FilePath filePrefix, List<MultipartFile> multipartFiles) {
public List<String> uploadFiles(FilePathConstants filePrefix, List<MultipartFile> multipartFiles) {
return multipartFiles.stream().map((file) -> uploadSingleFile(filePrefix, file)).toList();
}

public String uploadSingleFile(FilePath filePrefix, MultipartFile file) {
public String uploadSingleFile(FilePathConstants filePrefix, MultipartFile file) {
try {
Path filePath = getFilePath(file);
String objectKey = createObjectKey(filePrefix.getPath(), file.getOriginalFilename());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ public List<Member> findReviewers() {
.collect(Collectors.toList());
}

@Override
public Optional<Member> findReviewerById(Long id) {
Optional<MemberEntity> memberEntity = memberRepository.findByMemberIdAndIsReviewerTrue(id);
return memberEntity.map(memberPersistenceMapper::toDomain);
}

@Override
public void save(final Member member) {
MemberEntity memberEntity = memberPersistenceMapper.toEntity(member);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package clap.server.adapter.outbound.persistense;

import clap.server.adapter.inbound.web.dto.task.FilterAllTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterPendingApprovalResponse;
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.adapter.outbound.persistense.mapper.TaskPersistenceMapper;
Expand Down Expand Up @@ -63,6 +63,12 @@ public Slice<Task> findByProcessorAndStatus(Long processorId, List<TaskStatus> s
return tasks.map(taskPersistenceMapper::toDomain);
}

@Override
public Optional<Task> findByIdAndStatus(Long id, TaskStatus status) {
Optional<TaskEntity> taskEntity = taskRepository.findByTaskIdAndTaskStatus(id, status);
return taskEntity.map(taskPersistenceMapper::toDomain);
}

@Override
public List<Task> findYesterdayTaskByDate(LocalDateTime now) {
return taskRepository.findYesterdayTaskByUpdatedAtIsBetween(now.minusDays(1), now)
Expand All @@ -75,4 +81,16 @@ public Page<FilterAllTasksResponse> findAllTasks (Pageable pageable, FilterTaskL
.map(taskPersistenceMapper::toDomain);
return taskList.map(TaskMapper::toFilterAllTasksResponse);
}
@Override
public Optional<Task> findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder){
Optional<TaskEntity> taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder);
return taskEntity.map(taskPersistenceMapper::toDomain);
}

@Override
public Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder){
Optional<TaskEntity> taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder);
return taskEntity.map(taskPersistenceMapper::toDomain);
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package clap.server.adapter.outbound.persistense.entity.task.constant;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.List;

@Getter
@RequiredArgsConstructor
public enum TaskStatus {
Expand All @@ -16,10 +17,6 @@ public enum TaskStatus {

private final String description;

@JsonValue
public String getDescription() {
return description;
}
@JsonCreator
public static TaskStatus fromDescription(String description) {
for (TaskStatus status : TaskStatus.values()) {
Expand All @@ -29,4 +26,11 @@ public static TaskStatus fromDescription(String description) {
}
throw new IllegalArgumentException("Unknown description: " + description);
}

public static List<TaskStatus> getTaskBoardStatusList() {
return List.of(
TaskStatus.IN_PROGRESS,
TaskStatus.PENDING_COMPLETED,
TaskStatus.COMPLETED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ public interface MemberRepository extends JpaRepository<MemberEntity, Long> {
Optional<MemberEntity> findByNickname(String nickname);

List<MemberEntity> findByIsReviewerTrue();

Optional<MemberEntity> findByMemberIdAndIsReviewerTrue(Long memberId);
}

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Repository
public interface TaskRepository extends JpaRepository<TaskEntity, Long>, TaskCustomRepository {
Expand All @@ -28,15 +29,21 @@ List<TaskEntity> findYesterdayTaskByUpdatedAtIsBetween(
@Query("SELECT t FROM TaskEntity t " +
"WHERE t.processor.memberId = :processorId " +
"AND t.taskStatus IN :taskStatus " +
"AND (t.taskStatus != 'COMPLETED' OR t.finishedAt >= :untilDate)")
"AND (t.taskStatus != 'COMPLETED' OR t.finishedAt <= :untilDate) " +
"ORDER BY t.processorOrder ASC ")
Slice<TaskEntity> findTasksWithTaskStatusAndCompletedAt(
@Param("processorId") Long processorId,
@Param("taskStatus") List<TaskStatus> taskStatus,
@Param("untilDate") LocalDateTime untilDate,
Pageable pageable
);

Optional<TaskEntity> findByTaskIdAndTaskStatus(Long id, TaskStatus status);

Optional<TaskEntity> findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderDesc(Long processorId, TaskStatus taskStatus, Long processorOrder);

Optional<TaskEntity> findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc(
Long processorId, TaskStatus taskStatus, Long processorOrder);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public Member findActiveMember(Long memberId) {
() -> new ApplicationException(MemberErrorCode.ACTIVE_MEMBER_NOT_FOUND));
}

public Member findReviewer(Long memberId) {
return loadMemberPort.findReviewerById(memberId).orElseThrow(
()-> new ApplicationException(MemberErrorCode.NOT_A_REVIEWER)
);
}

public List<Member> findReviewers() {
return loadMemberPort.findReviewers();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@

public interface FindLabelListUsecase {

SliceResponse<FindLabelListResponse> findLabelList(Long userId, Pageable pageable);
List<FindLabelListResponse> findLabelListAdmin(Long userId);
SliceResponse<FindLabelListResponse> findLabelList(Long memberId, Pageable pageable);
List<FindLabelListResponse> findLabelListAdmin(Long memberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package clap.server.application.port.inbound.task;

import clap.server.adapter.inbound.web.dto.task.request.UpdateTaskOrderRequest;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;

public interface UpdateTaskBoardUsecase {
void updateTaskOrder(Long processorId, UpdateTaskOrderRequest request);
void updateTaskOrderAndStatus(Long processorId, UpdateTaskOrderRequest request, TaskStatus status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface LoadMemberPort {

List<Member> findReviewers();

Optional<Member> findReviewerById(Long id);

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package clap.server.application.port.outbound.s3;

import clap.server.domain.model.task.FilePath;
import clap.server.common.constants.FilePathConstants;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

public interface S3UploadPort {
List<String> uploadFiles(FilePath filePrefix, List<MultipartFile> multipartFiles);
List<String> uploadFiles(FilePathConstants filePrefix, List<MultipartFile> multipartFiles);

String uploadSingleFile(FilePath filePrefix, MultipartFile file);
String uploadSingleFile(FilePathConstants filePrefix, MultipartFile file);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package clap.server.application.port.outbound.task;

import clap.server.adapter.inbound.web.dto.task.FilterAllTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterPendingApprovalResponse;
import clap.server.adapter.inbound.web.dto.task.FilterAllTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
import clap.server.domain.model.task.Task;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -29,4 +26,10 @@ public interface LoadTaskPort {
Page<FilterAllTasksResponse> findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest);

Slice<Task> findByProcessorAndStatus(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDate, Pageable pageable);

Optional<Task> findByIdAndStatus(Long id, TaskStatus status);

Optional<Task> findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder);

Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder);
}
Loading