Skip to content

Commit 8ff66b0

Browse files
Merge remote-tracking branch 'upstream/develop'
2 parents 397ae84 + a26621f commit 8ff66b0

File tree

48 files changed

+707
-116
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+707
-116
lines changed

.github/workflows/deploy.yml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ jobs:
4545
runs-on: ubuntu-latest
4646

4747
steps:
48+
- name: Checkout repository
49+
uses: actions/checkout@v4
50+
4851
- name: Deploy to EC2
4952
env:
5053
EC2_SSH_KEY: ${{ secrets.EC2_SSH_KEY }}
@@ -55,13 +58,21 @@ jobs:
5558
run: |
5659
echo "$EC2_SSH_KEY" > private_key.pem
5760
chmod 600 private_key.pem
58-
61+
62+
mkdir -p ~/.ssh
63+
ssh-keyscan -H $EC2_HOST >> ~/.ssh/known_hosts
64+
chmod 600 ~/.ssh/known_hosts
65+
66+
# EC2로 docker-compose.yml 복사
67+
scp -i private_key.pem docker-compose.yml $EC2_USERNAME@$EC2_HOST:/home/$EC2_USERNAME/docker-compose.yml
68+
5969
ssh -i private_key.pem -o StrictHostKeyChecking=no $EC2_USERNAME@$EC2_HOST "
6070
docker pull $DOCKER_USERNAME/$MY_APP:latest
61-
docker stop $MY_APP || true
62-
docker rm $MY_APP || true
63-
docker run -d -p 8080:8080 --name $MY_APP \
64-
$DOCKER_USERNAME/$MY_APP:latest
71+
72+
docker-compose down || true
73+
74+
docker-compose up -d --remove-orphans
75+
docker image prune -f
6576
"
6677
6778
rm -f private_key.pem

build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ dependencies {
5555
implementation 'org.webjars:sockjs-client:1.0.2'
5656
implementation 'org.webjars:stomp-websocket:2.3.3'
5757
implementation 'org.webjars:jquery:3.1.1-1'
58+
59+
// s3 관련
60+
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
61+
62+
// redis
63+
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
5864
}
5965

6066
tasks.named('test') {

docker-compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
services:
2+
redis:
3+
image: redis
4+
container_name: redis
5+
ports:
6+
- "6379:6379"
7+
8+
spring:
9+
image: nimuy99/memesphere
10+
container_name: memesphere
11+
restart: always
12+
ports:
13+
- "8080:8080"
14+
depends_on:
15+
- redis

src/main/java/com/memesphere/domain/chartdata/repository/ChartDataRepository.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,24 @@
66
import org.springframework.data.jpa.repository.JpaRepository;
77
import org.springframework.data.jpa.repository.Query;
88

9+
import java.math.BigDecimal;
10+
import java.time.LocalDateTime;
911
import java.util.List;
1012

1113
public interface ChartDataRepository extends JpaRepository<ChartData, Long> {
12-
@Query("SELECT SUM(c.volume) FROM ChartData c")
13-
Long findTotalVolume();
14+
@Query("SELECT SUM(c.volume) FROM ChartData c " +
15+
"WHERE c.recordedTime = " +
16+
"(SELECT MAX(c2.recordedTime) FROM ChartData c2 WHERE c2.memeCoin = c.memeCoin)")
17+
BigDecimal findTotalVolume();
1418

15-
@EntityGraph(attributePaths = {"memeCoin"})
16-
List<ChartData> findTop5ByOrderByVolumeDesc();
19+
@Query("SELECT c FROM ChartData c " +
20+
"WHERE c.recordedTime = " +
21+
"(SELECT MAX(c2.recordedTime) FROM ChartData c2 WHERE c2.memeCoin = c.memeCoin) " +
22+
"ORDER BY c.volume DESC LIMIT 5")
23+
List<ChartData> findTop5OrderByVolumeDesc();
24+
25+
@Query("SELECT MAX(c.recordedTime) FROM ChartData c WHERE c.memeCoin.id = 1")
26+
LocalDateTime findRecordedTimeByCoinId1();
1727

1828
List<ChartData> findByMemeCoinOrderByRecordedTimeDesc(MemeCoin memeCoin);
1929
}

src/main/java/com/memesphere/domain/collection/controller/CollectionRestController.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
package com.memesphere.domain.collection.controller;
22

3+
import com.memesphere.domain.collection.service.CollectionCommandService;
34
import com.memesphere.global.apipayload.ApiResponse;
45
import com.memesphere.domain.collection.entity.Collection;
56
import com.memesphere.domain.collection.dto.response.CollectionPageResponse;
67
import com.memesphere.domain.collection.service.CollectionQueryService;
8+
import com.memesphere.global.jwt.CustomUserDetails;
9+
import com.memesphere.global.jwt.TokenProvider;
710
import com.memesphere.global.validation.annotation.CheckPage;
811
import io.swagger.v3.oas.annotations.Operation;
12+
import io.swagger.v3.oas.annotations.Parameter;
13+
import io.swagger.v3.oas.annotations.tags.Tag;
14+
import jakarta.servlet.http.HttpServletRequest;
915
import lombok.RequiredArgsConstructor;
1016
import org.springframework.data.domain.Page;
17+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1118
import org.springframework.web.bind.annotation.*;
1219
import org.springframework.web.bind.annotation.RestController;
1320
import com.memesphere.domain.collection.converter.CollectionConverter;
1421

22+
@Tag(name="콜렉션", description = "콜렉션 관련 API")
1523
@RestController
1624
@RequiredArgsConstructor
1725
//@RequestMapping("/collection")
1826
public class CollectionRestController {
1927
private final CollectionQueryService collectionQueryService;
28+
private final CollectionCommandService collectionCommandService;
29+
private final TokenProvider tokenProvider;
2030

2131
@GetMapping("/collection")
2232
@Operation(summary = "사용자의 밈코인 콜렉션 모음 조회 API")
@@ -32,4 +42,26 @@ public ApiResponse<CollectionPageResponse> getCollectionList (
3242
return ApiResponse.onSuccess(CollectionConverter.toCollectionPageDTO(collectionPage));
3343
}
3444

45+
@PostMapping("/collection/{coinId}")
46+
@Operation(summary = "밈코인 콜렉션 등록 API",
47+
description = "코인 Id를 입력하면 사용자의 콜렉션에 등록")
48+
public ApiResponse<String> postCollectCoin (@Parameter(hidden = true) @AuthenticationPrincipal CustomUserDetails customUserDetails,
49+
@PathVariable Long coinId) {
50+
51+
Long userId = customUserDetails.getUser().getId();
52+
53+
return ApiResponse.onSuccess(collectionCommandService.addCollectCoin(userId, coinId));
54+
}
55+
56+
@DeleteMapping("/collection/{coinId}")
57+
@Operation(summary = "밈코인 콜렉션 삭제 API",
58+
description = "코인 Id를 입력하면 사용자의 콜렉션에서 삭제")
59+
public ApiResponse<String> deleteCollectCoin (@Parameter(hidden = true) @AuthenticationPrincipal CustomUserDetails customUserDetails,
60+
@PathVariable Long coinId) {
61+
62+
Long userId = customUserDetails.getUser().getId();
63+
64+
return ApiResponse.onSuccess(collectionCommandService.removeCollectCoin(userId, coinId));
65+
}
66+
3567
}

src/main/java/com/memesphere/domain/collection/converter/CollectionConverter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@
55
import com.memesphere.domain.memecoin.entity.MemeCoin;
66
import com.memesphere.domain.collection.dto.response.CollectionPageResponse;
77
import com.memesphere.domain.collection.dto.response.CollectionPreviewResponse;
8+
import com.memesphere.domain.user.entity.User;
89
import org.springframework.data.domain.Page;
910

1011
import java.util.List;
1112
import java.util.stream.Collectors;
1213

1314
public class CollectionConverter {
15+
16+
public static Collection toCollection(User user, MemeCoin coin) {
17+
return Collection.builder()
18+
.user(user).memeCoin(coin).build();
19+
}
20+
1421
public static CollectionPageResponse toCollectionPageDTO(Page<Collection> collectionPage) {
1522
List<CollectionPreviewResponse> collectionItems = collectionPage.getContent().stream()
1623
.map(collection -> toCollectionPreviewDTO(collection))

src/main/java/com/memesphere/domain/collection/entity/Collection.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
@Entity
1212
@Getter
13+
@Builder
1314
@DynamicUpdate
1415
@DynamicInsert
1516
@AllArgsConstructor
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package com.memesphere.domain.collection.repository;
22

33
import com.memesphere.domain.collection.entity.Collection;
4+
import com.memesphere.domain.memecoin.entity.MemeCoin;
5+
import com.memesphere.domain.user.entity.User;
46
import org.springframework.data.domain.Page;
57
import org.springframework.data.domain.Pageable;
68
import org.springframework.data.jpa.repository.JpaRepository;
79

810
import java.util.List;
11+
import java.util.Optional;
912

1013
public interface CollectionRepository extends JpaRepository<Collection, Long> {
1114
Page<Collection> findAllByUserId(Long userId, Pageable pageable);
1215
List<Collection> findAllByUserId(Long userId);
16+
Optional<Collection> findByUserAndMemeCoin(User user, MemeCoin memeCoin);
1317
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.memesphere.domain.collection.service;
2+
3+
import com.memesphere.domain.collection.entity.Collection;
4+
import org.springframework.data.domain.Page;
5+
6+
import java.util.List;
7+
8+
public interface CollectionCommandService {
9+
String addCollectCoin(Long userId, Long coinId);
10+
String removeCollectCoin(Long userId, Long coinId);
11+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.memesphere.domain.collection.service;
2+
3+
import com.memesphere.domain.collection.converter.CollectionConverter;
4+
import com.memesphere.domain.collection.entity.Collection;
5+
import com.memesphere.domain.collection.repository.CollectionRepository;
6+
import com.memesphere.domain.memecoin.entity.MemeCoin;
7+
import com.memesphere.domain.memecoin.repository.MemeCoinRepository;
8+
import com.memesphere.domain.user.entity.User;
9+
import com.memesphere.domain.user.repository.UserRepository;
10+
import com.memesphere.global.apipayload.code.status.ErrorStatus;
11+
import com.memesphere.global.apipayload.exception.GeneralException;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.stereotype.Service;
14+
15+
@Service
16+
@RequiredArgsConstructor
17+
public class CollectionCommandServiceImpl implements CollectionCommandService {
18+
private final CollectionRepository collectionRepository;
19+
private final UserRepository userRepository;
20+
private final MemeCoinRepository coinRepository;
21+
22+
public String addCollectCoin(Long userId, Long coinId) {
23+
User user = userRepository.findById(userId)
24+
.orElseThrow(() -> new GeneralException(ErrorStatus.USER_NOT_FOUND));
25+
26+
MemeCoin coin = coinRepository.findById(coinId)
27+
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMECOIN_NOT_FOUND));
28+
29+
// 이미 등록한 콜렉션의 경우
30+
if (collectionRepository.findByUserAndMemeCoin(user, coin).isPresent()) {
31+
throw new GeneralException(ErrorStatus.COLLECTION_ALREADY_EXISTS);
32+
}
33+
34+
// 새로운 컬렉션 저장
35+
Collection collection = CollectionConverter.toCollection(user, coin);
36+
collectionRepository.save(collection);
37+
38+
return "[coinId] " + coinId + " 등록 완료.";
39+
}
40+
41+
@Override
42+
public String removeCollectCoin(Long userId, Long coinId) {
43+
User user = userRepository.findById(userId)
44+
.orElseThrow(() -> new GeneralException(ErrorStatus.USER_NOT_FOUND));
45+
46+
MemeCoin coin = coinRepository.findById(coinId)
47+
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMECOIN_NOT_FOUND));
48+
49+
// 이미 삭제한 콜렉션의 경우
50+
Collection collection = collectionRepository.findByUserAndMemeCoin(user, coin)
51+
.orElseThrow(() -> new GeneralException(ErrorStatus.COLLECTION_NOT_FOUND));
52+
53+
collectionRepository.delete(collection);
54+
55+
return "[coinId] " + coinId + " 삭제 완료.";
56+
}
57+
58+
}

0 commit comments

Comments
 (0)