Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M3-349 그룹전 픽셀 상세 정보 반환 api 구현 #77

Merged
merged 12 commits into from
Sep 11, 2024
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
11 changes: 11 additions & 0 deletions src/main/java/com/m3pro/groundflip/controller/PixelController.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.springframework.web.bind.annotation.RestController;

import com.m3pro.groundflip.domain.dto.Response;
import com.m3pro.groundflip.domain.dto.pixel.CommunityPixelInfoResponse;
import com.m3pro.groundflip.domain.dto.pixel.IndividualHistoryPixelResponse;
import com.m3pro.groundflip.domain.dto.pixel.IndividualModePixelResponse;
import com.m3pro.groundflip.domain.dto.pixel.IndividualPixelInfoResponse;
Expand Down Expand Up @@ -105,6 +106,16 @@ public Response<IndividualHistoryPixelInfoResponse> getIndividualPixelInfo(
);
}

@Operation(summary = "그룹전 픽셀 정보 조회", description = "특정 그룹전 픽셀의 정보를 조회 API")
@GetMapping("/community-mode/{pixelId}")
public Response<CommunityPixelInfoResponse> getCommunityPixelInfo(
@Parameter(description = "찾고자 하는 pixelId", required = true)
@PathVariable Long pixelId) {
return Response.createSuccess(
pixelReader.getCommunityModePixelInfo(pixelId)
);
}

@Operation(summary = "픽셀 차지", description = "특정 픽셀의 id, 사용자 id, 커뮤니티 id를 사용해 소유권을 바꾸는 API ")
@PostMapping("")
public Response<?> occupyPixel(@RequestBody PixelOccupyRequest pixelOccupyRequest) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.m3pro.groundflip.domain.dto.pixel;

import java.util.List;

import com.m3pro.groundflip.domain.entity.Pixel;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@Schema(title = "그룹전 픽셀 정보")
public class CommunityPixelInfoResponse {
private String address;
private Integer addressNumber;
private Integer visitCount;
private PixelOwnerCommunityResponse pixelOwnerCommunity;
private List<VisitedCommunityInfo> visitList;

public static CommunityPixelInfoResponse from(Pixel pixel, PixelOwnerCommunityResponse pixelOwnerCommunityResponse,
List<VisitedCommunityInfo> visitedCommunityList) {
String realAddress;

if (pixel.getAddress() != null) {
String[] addressArr = pixel.getAddress().split(" ");
if (addressArr[0].equals("대한민국")) {
realAddress = addressArr[0];
} else {
realAddress = addressArr[1] + ' ' + addressArr[2];
}
} else {
realAddress = null;
}

return CommunityPixelInfoResponse.builder()
.address(realAddress)
.addressNumber(pixel.getAddressNumber())
.visitCount(visitedCommunityList.size())
.pixelOwnerCommunity(pixelOwnerCommunityResponse)
.visitList(visitedCommunityList)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.m3pro.groundflip.domain.dto.pixel;

import com.m3pro.groundflip.domain.entity.Community;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class PixelOwnerCommunityResponse {
private Long communityId;
private String name;
private String profileImageUrl;
private Long currentPixelCount;
private Long accumulatePixelCount;

public static PixelOwnerCommunityResponse from(Community pixelOwnerCommunity, Long currentPixelCount,
Long accumulatePixelCount) {
if (pixelOwnerCommunity == null) {
return null;
} else {
return PixelOwnerCommunityResponse.builder()
.communityId(pixelOwnerCommunity.getId())
.name(pixelOwnerCommunity.getName())
.profileImageUrl(pixelOwnerCommunity.getBackgroundImageUrl())
.accumulatePixelCount(accumulatePixelCount)
.currentPixelCount(currentPixelCount)
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.m3pro.groundflip.domain.dto.pixel;

import com.m3pro.groundflip.domain.dto.pixelUser.VisitedCommunity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class VisitedCommunityInfo {
private String name;
private String profileImageUrl;

public static VisitedCommunityInfo from(VisitedCommunity visitedCommunity) {
return VisitedCommunityInfo.builder()
.name(visitedCommunity.getName())
.profileImageUrl(visitedCommunity.getProfileImage())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.m3pro.groundflip.domain.dto.pixelUser;

public interface VisitedCommunity {
Long getPixelId();

Long getCommunityId();

String getName();

String getProfileImage();
}
Original file line number Diff line number Diff line change
@@ -1,77 +1,11 @@
package com.m3pro.groundflip.repository;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Repository;

import com.m3pro.groundflip.domain.dto.ranking.Ranking;

import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class CommunityRankingRedisRepository {
private static final String CURRENT_PIXEL_RANKING_KEY = "group_current_pixel_ranking";
private static final String ACCUMULATE_PIXEL_RANKING_KEY = "group_accumulate_pixel_ranking";
private static final int RANKING_START_INDEX = 0;
private static final int RANKING_END_INDEX = 29;
private final RedisTemplate<String, String> redisTemplate;
private ZSetOperations<String, String> zSetOperations;

@PostConstruct
void init() {
zSetOperations = redisTemplate.opsForZSet();
}

public void increaseCurrentPixelCount(Long communityId) {
zSetOperations.incrementScore(CURRENT_PIXEL_RANKING_KEY, communityId.toString(), 1);
}

public void decreaseCurrentPixelCount(Long communityId) {
Double currentScore = zSetOperations.score(CURRENT_PIXEL_RANKING_KEY, communityId.toString());
if (currentScore != null && currentScore > 0) {
zSetOperations.incrementScore(CURRENT_PIXEL_RANKING_KEY, communityId.toString(), -1);
}
}

public void increaseAccumulatePixelCount(Long communityId) {
zSetOperations.incrementScore(ACCUMULATE_PIXEL_RANKING_KEY, communityId.toString(), 1);
}

public List<Ranking> getRankingsWithCurrentPixelCount() {
Set<ZSetOperations.TypedTuple<String>> typedTuples = zSetOperations.reverseRangeWithScores(
CURRENT_PIXEL_RANKING_KEY,
RANKING_START_INDEX, RANKING_END_INDEX);
if (typedTuples == null) {
return new ArrayList<>();
}

List<Ranking> rankings = new ArrayList<>();
long rank = 1;
for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
rankings.add(Ranking.from(typedTuple, rank++));
}
return rankings;
}

public Optional<Long> getCommunityCurrentPixelRank(Long communityId) {
Long rank = zSetOperations.reverseRank(CURRENT_PIXEL_RANKING_KEY, communityId.toString());
return Optional.ofNullable(rank).map(r -> r + 1);
}

public Optional<Long> getCommunityCurrentPixelCount(Long communityId) {
Double currentPixelCount = zSetOperations.score(CURRENT_PIXEL_RANKING_KEY, communityId.toString());
return Optional.ofNullable(currentPixelCount).map(Double::longValue);
}

public Optional<Long> getCommunityAccumulatePixelCount(Long communityId) {
Double accumulatePixelCount = zSetOperations.score(ACCUMULATE_PIXEL_RANKING_KEY, communityId.toString());
return Optional.ofNullable(accumulatePixelCount).map(Double::longValue);
public class CommunityRankingRedisRepository extends RankingRedisRepository {
public CommunityRankingRedisRepository(RedisTemplate<String, String> redisTemplate) {
super(redisTemplate, "group_current_pixel_ranking", "group_accumulate_pixel_ranking");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.m3pro.groundflip.domain.dto.pixelUser.VisitedCommunity;
import com.m3pro.groundflip.domain.dto.pixelUser.VisitedUser;
import com.m3pro.groundflip.domain.entity.Pixel;
import com.m3pro.groundflip.domain.entity.PixelUser;
Expand All @@ -27,6 +28,19 @@ public interface PixelUserRepository extends JpaRepository<PixelUser, Long> {
List<VisitedUser> findAllVisitedUserByPixelId(
@Param("pixel_id") Long pixelId);

@Query(value = """
SELECT pu.pixel_id AS pixelId,
pu.community_id AS communityId,
c.name AS name,
c.background_image_url AS profileImage
FROM pixel_user pu
JOIN community c ON pu.community_id = c.community_id
WHERE pu.pixel_id = :pixel_id AND pu.created_at >= current_date()
GROUP BY pu.community_id;
""", nativeQuery = true)
List<VisitedCommunity> findAllVisitedCommunityByPixelId(
@Param("pixel_id") Long pixelId);

@Query(value = """
SELECT COUNT(DISTINCT pu.pixel.id) AS count
FROM PixelUser pu
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.m3pro.groundflip.repository;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

import com.m3pro.groundflip.domain.dto.ranking.Ranking;

public class RankingRedisRepository {
private static final int RANKING_START_INDEX = 0;
private static final int RANKING_END_INDEX = 29;
private final String currentPixelRankingKey;
private final String accumulatePixelRankingKey;
private final ZSetOperations<String, String> zSetOperations;

public RankingRedisRepository(RedisTemplate<String, String> redisTemplate, String currentPixelRankingKey,
String accumulatePixelRankingKey) {
this.currentPixelRankingKey = currentPixelRankingKey;
this.accumulatePixelRankingKey = accumulatePixelRankingKey;
zSetOperations = redisTemplate.opsForZSet();
}

public void increaseCurrentPixelCount(Long userId) {
zSetOperations.incrementScore(currentPixelRankingKey, userId.toString(), 1);
}

public void increaseAccumulatePixelCount(Long userId) {
zSetOperations.incrementScore(accumulatePixelRankingKey, userId.toString(), 1);
}

public void decreaseCurrentPixelCount(Long userId) {
Double currentScore = zSetOperations.score(currentPixelRankingKey, userId.toString());
if (currentScore != null && currentScore > 0) {
zSetOperations.incrementScore(currentPixelRankingKey, userId.toString(), -1);
}
}

public void saveUserInRanking(Long userId) {
Double currentPixelScore = zSetOperations.score(currentPixelRankingKey, userId.toString());
Double accumulatePixelScore = zSetOperations.score(accumulatePixelRankingKey, userId.toString());

if (currentPixelScore == null) {
zSetOperations.add(currentPixelRankingKey, userId.toString(), 0);
}
if (accumulatePixelScore == null) {
zSetOperations.add(accumulatePixelRankingKey, userId.toString(), 0);
}
}

public void deleteUserInRanking(Long userId) {
Double currentPixelScore = zSetOperations.score(currentPixelRankingKey, userId.toString());
Double accumulatePixelScore = zSetOperations.score(accumulatePixelRankingKey, userId.toString());

if (currentPixelScore != null) {
zSetOperations.remove(currentPixelRankingKey, userId.toString());
}
if (accumulatePixelScore == null) {
zSetOperations.remove(accumulatePixelRankingKey, userId.toString());
}
}

public List<Ranking> getRankingsWithCurrentPixelCount() {
Set<ZSetOperations.TypedTuple<String>> typedTuples = zSetOperations.reverseRangeWithScores(
currentPixelRankingKey,
RANKING_START_INDEX, RANKING_END_INDEX);
if (typedTuples == null) {
return new ArrayList<>();
}

List<Ranking> rankings = new ArrayList<>();
long rank = 1;
for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
rankings.add(Ranking.from(typedTuple, rank++));
}
return rankings;
}

public Optional<Long> getCurrentPixelRank(Long userId) {
Long rank = zSetOperations.reverseRank(currentPixelRankingKey, userId.toString());
return Optional.ofNullable(rank).map(r -> r + 1);
}

public Optional<Long> getCurrentPixelCount(Long userId) {
Double currentPixelCount = zSetOperations.score(currentPixelRankingKey, userId.toString());
return Optional.ofNullable(currentPixelCount).map(Double::longValue);
}

public Optional<Long> getAccumulatePixelCount(Long userId) {
Double accumulatePixelCount = zSetOperations.score(accumulatePixelRankingKey, userId.toString());
return Optional.ofNullable(accumulatePixelCount).map(Double::longValue);
}
}
Loading
Loading