diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..e1ee604 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,29 @@ +name: Deploy To EC2 + +on: + push: + branches: + - master + +jobs: + Deploy: + runs-on: ubuntu-latest + steps: + - name: SSH(원격 접속)로 EC2에 접속하기 + uses: appleboy/ssh-action@v1.0.3 + env: + APPLICATION_PROPERTIES: ${{ secrets.APPLICATION_PROPERTIES }} + with: + host: ${{ secrets.EC2_HOST }} + username: ${{ secrets.EC2_USERNAME }} + key: ${{ secrets.EC2_PRIVATE_KEY }} + envs: APPLICATION_PROPERTIES + script_stop: true + script: | + cd /home/ubuntu/LostArk + rm -rf src/main/resources/application.properties + git pull origin master + echo "$APPLICATION_PROPERTIES" > src/main/resources/application.properties + ./gradlew clean build + docker compose -f compose-prod.yml down + docker compose -f compose-prod.yml up --build -d diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ffa2d1b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17-jdk + +COPY build/libs/*SNAPSHOT.jar app.jar + +ENTRYPOINT ["java", "-jar", "/app.jar"] \ No newline at end of file diff --git a/Dockerfile-prod b/Dockerfile-prod new file mode 100644 index 0000000..06fd374 --- /dev/null +++ b/Dockerfile-prod @@ -0,0 +1,5 @@ +FROM openjdk:17-jdk + +COPY build/libs/*SNAPSHOT.jar app.jar + +ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "/app.jar"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..42500ad --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +로스트아크 직업 추천 팀 프로제트 diff --git a/build.gradle b/build.gradle index 11fbf2c..725c237 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,10 @@ dependencies { runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation("com.googlecode.json-simple:json-simple:1.1.1") + implementation 'org.springframework.boot:spring-boot-starter-data-redis' } tasks.named('test') { diff --git a/compose-prod.yml b/compose-prod.yml new file mode 100644 index 0000000..3d026d0 --- /dev/null +++ b/compose-prod.yml @@ -0,0 +1,19 @@ +services: + api-server: + build: + context: . + dockerfile: ./Dockerfile-prod + ports: + - 8080:8080 + depends_on: + cache-server: + condition: service_healthy + cache-server: + image: redis + command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru + ports: + - 6379:6379 + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 5s + retries: 10 diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..3a9bbb7 --- /dev/null +++ b/compose.yml @@ -0,0 +1,16 @@ +services: + api-server: + build: . + ports: + - 8080:8080 + depends_on: + cache-server: + condition: service_healthy + cache-server: + image: redis + ports: + - 6379:6379 + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 5s + retries: 10 \ No newline at end of file diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/Job/JobAttributes.java b/src/main/java/com/TeamNull/LostArk/LostArk/Job/JobAttributes.java new file mode 100644 index 0000000..ca18690 --- /dev/null +++ b/src/main/java/com/TeamNull/LostArk/LostArk/Job/JobAttributes.java @@ -0,0 +1,41 @@ +package com.TeamNull.LostArk.LostArk.Job; + + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +public class JobAttributes { + private final String jobName; + private final int Agreeableness; + private final int conscientiousness; + private final int extraversion; + private final int openness; + private final int neuroticism; + private final String color; + private final String icon; + + @JsonCreator + public JobAttributes( + @JsonProperty("jobName") String jobName, + @JsonProperty("Agreeableness") int Agreeableness, + @JsonProperty("conscientiousness") int conscientiousness, + @JsonProperty("extraversion") int extraversion, + @JsonProperty("openness") int openness, + @JsonProperty("neuroticism") int neuroticism, + @JsonProperty("color") String color, + @JsonProperty("icon") String icon) { + this.jobName = jobName; + this.Agreeableness = Agreeableness; + this.conscientiousness = conscientiousness; + this.extraversion = extraversion; + this.openness = openness; + this.neuroticism = neuroticism; + this.color = color; + this.icon = icon; + } +} diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/Job/TopFactor.java b/src/main/java/com/TeamNull/LostArk/LostArk/Job/TopFactor.java new file mode 100644 index 0000000..bd41464 --- /dev/null +++ b/src/main/java/com/TeamNull/LostArk/LostArk/Job/TopFactor.java @@ -0,0 +1,18 @@ +package com.TeamNull.LostArk.LostArk.Job; + +import jakarta.persistence.Embeddable; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Embeddable +public class TopFactor { + private String jobName; + private double value; + + public TopFactor(String jobName, double value) { + this.jobName = jobName; + this.value = value; + } +} diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/Job/TopFactorConverter.java b/src/main/java/com/TeamNull/LostArk/LostArk/Job/TopFactorConverter.java new file mode 100644 index 0000000..28d6978 --- /dev/null +++ b/src/main/java/com/TeamNull/LostArk/LostArk/Job/TopFactorConverter.java @@ -0,0 +1,21 @@ +package com.TeamNull.LostArk.LostArk.Job; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter +public class TopFactorConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(TopFactor topFactor) { + // 예: "jobName:value" 형식으로 직렬화 + return topFactor.getJobName() + ":" + topFactor.getValue(); + } + + @Override + public TopFactor convertToEntityAttribute(String dbData) { + // 역직렬화: "jobName:value" 형식을 객체로 변환 + String[] data = dbData.split(":"); + return new TopFactor(data[0], Double.parseDouble(data[1])); + } +} diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/LostArkApplication.java b/src/main/java/com/TeamNull/LostArk/LostArk/LostArkApplication.java index 04f8f8c..4dd6a9f 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/LostArkApplication.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/LostArkApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @SpringBootApplication public class LostArkApplication { diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/config/RedisCacheConfig.java b/src/main/java/com/TeamNull/LostArk/LostArk/config/RedisCacheConfig.java new file mode 100644 index 0000000..a6f2239 --- /dev/null +++ b/src/main/java/com/TeamNull/LostArk/LostArk/config/RedisCacheConfig.java @@ -0,0 +1,42 @@ +package com.TeamNull.LostArk.LostArk.config; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; + +@Configuration +@EnableCaching // Spring Boot의 캐싱 설정을 활성화 +public class RedisCacheConfig { + @Bean + public CacheManager commentCacheManager(RedisConnectionFactory redisConnectionFactory) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration + .defaultCacheConfig() + // Redis에 Key를 저장할 때 String으로 직렬화(변환)해서 저장 + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new StringRedisSerializer())) + // Redis에 Value를 저장할 때 Json으로 직렬화(변환)해서 저장 + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new Jackson2JsonRedisSerializer(Object.class) + ) + ) + // 데이터의 만료기간(TTL) 설정 + .entryTtl(Duration.ofMinutes(1L)); + + return RedisCacheManager + .RedisCacheManagerBuilder + .fromConnectionFactory(redisConnectionFactory) + .cacheDefaults(redisCacheConfiguration) + .build(); + } +} diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/config/RedisConfig.java b/src/main/java/com/TeamNull/LostArk/LostArk/config/RedisConfig.java new file mode 100644 index 0000000..637038f --- /dev/null +++ b/src/main/java/com/TeamNull/LostArk/LostArk/config/RedisConfig.java @@ -0,0 +1,23 @@ +package com.TeamNull.LostArk.LostArk.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; + +@Configuration +public class RedisConfig { + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Bean + public LettuceConnectionFactory redisConnectionFactory() { + // Lettuce라는 라이브러리를 활용해 Redis 연결을 관리하는 객체를 생성하고 + // Redis 서버에 대한 정보(host, port)를 설정한다. + return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port)); + } +} \ No newline at end of file diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/config/WebConfig.java b/src/main/java/com/TeamNull/LostArk/LostArk/config/WebConfig.java new file mode 100644 index 0000000..a0fd915 --- /dev/null +++ b/src/main/java/com/TeamNull/LostArk/LostArk/config/WebConfig.java @@ -0,0 +1,25 @@ +package com.TeamNull.LostArk.LostArk.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins( + "http://localhost:3000", + "http://52.79.232.220:3000", + "http://classtest.site", + "http://la.classtest.site", + "https://classtest.site", + "https://la.classtest.site" + ) + .allowedMethods("GET", "POST", "PUT", "DELETE") + .allowedHeaders("*") + .allowCredentials(true); + } +} diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/controller/CommentController.java b/src/main/java/com/TeamNull/LostArk/LostArk/controller/CommentController.java index e6b0727..1203bd2 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/controller/CommentController.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/controller/CommentController.java @@ -1,4 +1,66 @@ package com.TeamNull.LostArk.LostArk.controller; +import com.TeamNull.LostArk.LostArk.dto.CommentDto; +import com.TeamNull.LostArk.LostArk.service.CommentService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + +import java.util.*; + + +@RestController +@RequestMapping("/api/comment") +@RequiredArgsConstructor public class CommentController { + + private final CommentService commentService; + + @Cacheable(cacheNames = "getComments", key = "'comments:page:' + #page + ':searchText:' + #searchText", cacheManager = "commentCacheManager") + @GetMapping("/{page}") + public Map commentPage(@PathVariable Integer page, + @RequestParam(required = false) String searchText, + @PageableDefault(size = 5, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) { + return commentService.getComments(page, searchText, pageable); + } + + + @PostMapping("/{userID}") + @CacheEvict(cacheNames = "getComments", allEntries = true, cacheManager = "commentCacheManager") + public void addComment(@PathVariable("userID") UUID userID, @RequestBody CommentDto commentDto) + { + commentService.getAddComment(commentDto.getContent(), + commentDto.getPassword(), + commentDto.getNickname(), + userID + ); + } + + @DeleteMapping("/delete/{userID}/{commentId}") + @CacheEvict(cacheNames = "getComments", allEntries = true, cacheManager = "commentCacheManager") + public ResponseEntity commentDelete(@PathVariable("userID") UUID userID, + @PathVariable Integer commentId, + @RequestBody CommentDto dropComment) + { + return commentService.getCommentDelete(userID,commentId,dropComment.getPassword()); + } + + @PutMapping("/update/{userID}/{commentId}") + @CacheEvict(cacheNames = "getComments", allEntries = true, cacheManager = "commentCacheManager") + public ResponseEntity commentUpdate(@PathVariable("userID") UUID userID, + @PathVariable int commentId, + @RequestBody CommentDto updatedComment) { + return commentService.getCommentUpdate(userID, commentId, updatedComment.getPassword(), updatedComment.getContent()); + } + } + + + + diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/controller/DataController.java b/src/main/java/com/TeamNull/LostArk/LostArk/controller/DataController.java index f847cc0..7bac495 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/controller/DataController.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/controller/DataController.java @@ -1,4 +1,32 @@ package com.TeamNull.LostArk.LostArk.controller; +import com.TeamNull.LostArk.LostArk.dto.DataDto; +import com.TeamNull.LostArk.LostArk.service.DataService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api") public class DataController { + + private final DataService dataService; + + @GetMapping("/statistics/data") + + public List read () { + List resData = null; + try { + resData = dataService.read(); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + return resData; + } } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/controller/OuterDataController.java b/src/main/java/com/TeamNull/LostArk/LostArk/controller/OuterDataController.java index cabd066..a1e2e06 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/controller/OuterDataController.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/controller/OuterDataController.java @@ -1,4 +1,40 @@ package com.TeamNull.LostArk.LostArk.controller; +import com.TeamNull.LostArk.LostArk.dto.OuterDataDto; +import com.TeamNull.LostArk.LostArk.service.OuterDataService; +import lombok.RequiredArgsConstructor; +import org.json.simple.parser.ParseException; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api") public class OuterDataController { + + private final OuterDataService outerDataService; + + // 목표 : Get 요청 시 api로부터 데이터를 받아와 저장 후 반환하도록 구현. + @GetMapping("/statistics/alluser") + public List alluser() { + List resData = null; + try { + resData = outerDataService.read(); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + return resData; + } + + // 데이터 저장 테스트용 메서드 + @PutMapping("/statistics/alluser") + public void createAlluser() throws IOException, ParseException{ + outerDataService.create(); + } + + } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/controller/ResultController.java b/src/main/java/com/TeamNull/LostArk/LostArk/controller/ResultController.java index 8f4f878..3370b6f 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/controller/ResultController.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/controller/ResultController.java @@ -1,4 +1,41 @@ package com.TeamNull.LostArk.LostArk.controller; +import com.TeamNull.LostArk.LostArk.dto.ResultDto; +import com.TeamNull.LostArk.LostArk.repository.ResultRepository; +import com.TeamNull.LostArk.LostArk.repository.UserRepository; +import com.TeamNull.LostArk.LostArk.service.ResultService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api") public class ResultController { -} + + private final ResultRepository resultRepository; + private final ResultService resultService; + private final UserRepository userRepository; + + @PostMapping("/results/{id}") + public void top5 (@PathVariable UUID id){ + resultService.top5(id); + } + + + // user UUID로 찾기 + @GetMapping("/results/{id}") + public ResponseEntity> result(@PathVariable("id") UUID id) { + List result = resultService.getResult(id); + if(result != null){ + return new ResponseEntity<>(result, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/controller/UserController.java b/src/main/java/com/TeamNull/LostArk/LostArk/controller/UserController.java index afe223e..5efc994 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/controller/UserController.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/controller/UserController.java @@ -1,4 +1,34 @@ package com.TeamNull.LostArk.LostArk.controller; +import com.TeamNull.LostArk.LostArk.dto.UserDto; +import com.TeamNull.LostArk.LostArk.entity.User; +import com.TeamNull.LostArk.LostArk.repository.UserRepository; +import com.TeamNull.LostArk.LostArk.service.UserService; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/users") public class UserController { -} + + private final UserRepository userRepository; + private final UserService userService; + + @PostMapping + public ResponseEntity createUser(@RequestBody UserDto userDto) { + User newUser = userService.saveUser(userDto); + return ResponseEntity.ok(newUser.getId()); + } + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable UUID id) { + // id로 User 객체를 찾아 반환 + return ResponseEntity.ok(userRepository.findById(id).get()); + } +} \ No newline at end of file diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/dto/CommentDto.java b/src/main/java/com/TeamNull/LostArk/LostArk/dto/CommentDto.java index 6c7dd0f..459a03c 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/dto/CommentDto.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/dto/CommentDto.java @@ -1,4 +1,64 @@ package com.TeamNull.LostArk.LostArk.dto; +import lombok.Data; +import java.sql.Timestamp; +import java.util.UUID; + +@Data public class CommentDto { -} + private int id; + private Timestamp createdAt; + private String content; + private String password; + private String nickname; + private UUID userID; // User 대신 UUID로 변경 + private Timestamp updatedAt; + + // Jackson이 직렬화/역직렬화를 위해 기본 생성자가 필요합니다. + public CommentDto() {} + + public CommentDto(int id, + Timestamp createdAt, + Timestamp updatedAt, + String content, + String password, + String nickname, + UUID userID) { + this.id = id; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + this.content = content; + this.password = password; + this.nickname = nickname; + this.userID = userID; + } + + @Data + public static class CommentResponseDto { + private int id; + private Timestamp createdAt; + private String content; + private UUID userID; + private String topFactorResult; + private String nickname; + + // 기본 생성자 + public CommentResponseDto() {} + + public CommentResponseDto(int id, + Timestamp createdAt, + String content, + UUID userID, + String topFactorResult, + String nickname) { + this.id = id; + this.createdAt = createdAt; + this.content = content; + this.userID = userID; + this.topFactorResult = topFactorResult; + this.nickname = nickname; + } + } + + +} \ No newline at end of file diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/dto/DataDTo.java b/src/main/java/com/TeamNull/LostArk/LostArk/dto/DataDTo.java deleted file mode 100644 index 2c41861..0000000 --- a/src/main/java/com/TeamNull/LostArk/LostArk/dto/DataDTo.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.TeamNull.LostArk.LostArk.dto; - -public class DataDTo { -} diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/dto/DataDto.java b/src/main/java/com/TeamNull/LostArk/LostArk/dto/DataDto.java new file mode 100644 index 0000000..8d38635 --- /dev/null +++ b/src/main/java/com/TeamNull/LostArk/LostArk/dto/DataDto.java @@ -0,0 +1,23 @@ +package com.TeamNull.LostArk.LostArk.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.sql.Timestamp; + +@Getter +@Setter +public class DataDto { + + private String name; + private int value; + private String icon; + + public DataDto(String name, int value, String icon) { + this.name = name; + this.value = value; + this.icon = icon; + } +} diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/dto/OuterDataDto.java b/src/main/java/com/TeamNull/LostArk/LostArk/dto/OuterDataDto.java index ff21357..c0aef88 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/dto/OuterDataDto.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/dto/OuterDataDto.java @@ -1,4 +1,22 @@ package com.TeamNull.LostArk.LostArk.dto; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.net.URL; +import java.sql.Timestamp; + +@Getter +@Setter public class OuterDataDto { + private String name; + private int value; + private String icon; + + public OuterDataDto(String name, int value, String icon) { + this.name = name; + this.value = value; + this.icon = icon; + } } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/dto/ResultDto.java b/src/main/java/com/TeamNull/LostArk/LostArk/dto/ResultDto.java index 6efc046..12d0781 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/dto/ResultDto.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/dto/ResultDto.java @@ -1,4 +1,22 @@ package com.TeamNull.LostArk.LostArk.dto; +import com.TeamNull.LostArk.LostArk.Job.TopFactor; +import com.TeamNull.LostArk.LostArk.entity.User; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter public class ResultDto { + private String name; + private double value; + private String icon; + private String color; + + public ResultDto(String name, double value, String icon, String color) { + this.name = name; + this.value = value; + this.icon = icon; + this.color = color; + } } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/dto/UserDto.java b/src/main/java/com/TeamNull/LostArk/LostArk/dto/UserDto.java index e0c48e0..5cf4d95 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/dto/UserDto.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/dto/UserDto.java @@ -1,4 +1,23 @@ package com.TeamNull.LostArk.LostArk.dto; +import lombok.*; +import java.util.UUID; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor public class UserDto { + + private UUID Id; + private int Question1; + private int Question2; + private int Question3; + private int Question4; + private int Question5; + private int Question6; + private int Question7; + private int Question8; + private int Question9; + private int Question10; } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/entity/Comment.java b/src/main/java/com/TeamNull/LostArk/LostArk/entity/Comment.java index d79d81a..b60aa49 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/entity/Comment.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/entity/Comment.java @@ -1,8 +1,11 @@ package com.TeamNull.LostArk.LostArk.entity; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; import java.sql.Timestamp; @@ -15,16 +18,19 @@ public class Comment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ID") + @JsonIgnore private int id; @ManyToOne - @JoinColumn(name = "UserID", referencedColumnName = "ID") + @JoinColumn(name = "userID", referencedColumnName = "ID") private User user; @Column(name = "Password", length = 255) + @JsonIgnore private String password; @Column(name = "CreatedAt") + @CreationTimestamp private Timestamp createdAt; @Column(name = "TopFactorResult", length = 255) @@ -33,4 +39,11 @@ public class Comment { @Column(name = "Content", columnDefinition = "TEXT") private String content; + @Column(name="nickname") + private String nickName; + + @Column(name = "updatedAt") + @UpdateTimestamp + private Timestamp updatedAt; + } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/entity/Result.java b/src/main/java/com/TeamNull/LostArk/LostArk/entity/Result.java index ff9eed0..32d9539 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/entity/Result.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/entity/Result.java @@ -1,5 +1,7 @@ package com.TeamNull.LostArk.LostArk.entity; +import com.TeamNull.LostArk.LostArk.Job.TopFactor; +import com.TeamNull.LostArk.LostArk.Job.TopFactorConverter; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; @@ -19,18 +21,23 @@ public class Result { @JoinColumn(name = "UserID", referencedColumnName = "ID", nullable = false) private User user; + @Convert(converter = TopFactorConverter.class) @Column(name = "TopFactor1", length = 255) - private String topFactor1; + private TopFactor topFactor1; + @Convert(converter = TopFactorConverter.class) @Column(name = "TopFactor2", length = 255) - private String topFactor2; + private TopFactor topFactor2; + @Convert(converter = TopFactorConverter.class) @Column(name = "TopFactor3", length = 255) - private String topFactor3; + private TopFactor topFactor3; + @Convert(converter = TopFactorConverter.class) @Column(name = "TopFactor4", length = 255) - private String topFactor4; + private TopFactor topFactor4; + @Convert(converter = TopFactorConverter.class) @Column(name = "TopFactor5", length = 255) - private String topFactor5; + private TopFactor topFactor5; } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/entity/User.java b/src/main/java/com/TeamNull/LostArk/LostArk/entity/User.java index c187e98..b56a198 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/entity/User.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/entity/User.java @@ -1,8 +1,11 @@ package com.TeamNull.LostArk.LostArk.entity; import jakarta.persistence.*; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.annotations.Check; import java.util.UUID; @@ -10,40 +13,52 @@ @Table(name = "Users") @Getter @Setter +@NoArgsConstructor +@AllArgsConstructor public class User { + @Id - @GeneratedValue - @Column(name = "ID", columnDefinition = "BINARY(16)") + @GeneratedValue(strategy = GenerationType.AUTO) private UUID id; @Column(name = "Question1") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question1; @Column(name = "Question2") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question2; @Column(name = "Question3") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question3; @Column(name = "Question4") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question4; @Column(name = "Question5") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question5; @Column(name = "Question6") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question6; @Column(name = "Question7") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question7; @Column(name = "Question8") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question8; @Column(name = "Question9") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question9; @Column(name = "Question10") + @Check(constraints = "Question BETWEEN 1 AND 5") private int question10; } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/repository/CommentRepository.java b/src/main/java/com/TeamNull/LostArk/LostArk/repository/CommentRepository.java index 67e5284..7790299 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/repository/CommentRepository.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/repository/CommentRepository.java @@ -1,4 +1,18 @@ package com.TeamNull.LostArk.LostArk.repository; -public interface CommentRepository { + +import com.TeamNull.LostArk.LostArk.entity.Comment; +import com.TeamNull.LostArk.LostArk.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface CommentRepository extends JpaRepository { + Page findAll(Pageable pageable); + Optional findByUserIdAndId(UUID userID, int id); + List findByTopFactorResultContainingIgnoreCase(String topFactorResult); } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/repository/DataRepository.java b/src/main/java/com/TeamNull/LostArk/LostArk/repository/DataRepository.java index a56ead7..f1189eb 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/repository/DataRepository.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/repository/DataRepository.java @@ -1,4 +1,7 @@ package com.TeamNull.LostArk.LostArk.repository; -public interface DataRepository { +import com.TeamNull.LostArk.LostArk.entity.Data; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DataRepository extends JpaRepository { } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/repository/OuterDataRepository.java b/src/main/java/com/TeamNull/LostArk/LostArk/repository/OuterDataRepository.java index e3921e6..b2b4059 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/repository/OuterDataRepository.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/repository/OuterDataRepository.java @@ -1,4 +1,7 @@ package com.TeamNull.LostArk.LostArk.repository; -public interface OuterDataRepository { +import com.TeamNull.LostArk.LostArk.entity.OuterData; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OuterDataRepository extends JpaRepository { } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/repository/ResultRepository.java b/src/main/java/com/TeamNull/LostArk/LostArk/repository/ResultRepository.java index 79ff10e..be1633e 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/repository/ResultRepository.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/repository/ResultRepository.java @@ -1,4 +1,11 @@ package com.TeamNull.LostArk.LostArk.repository; -public interface ResultRepository { +import com.TeamNull.LostArk.LostArk.entity.Result; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; +import java.util.UUID; + +public interface ResultRepository extends JpaRepository { + public Optional findByUserId(UUID id); } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/repository/UserRepository.java b/src/main/java/com/TeamNull/LostArk/LostArk/repository/UserRepository.java index e95905b..6687db9 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/repository/UserRepository.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/repository/UserRepository.java @@ -1,4 +1,9 @@ package com.TeamNull.LostArk.LostArk.repository; -public interface UserRepository { +import com.TeamNull.LostArk.LostArk.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface UserRepository extends JpaRepository { } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/service/CommentService.java b/src/main/java/com/TeamNull/LostArk/LostArk/service/CommentService.java index 1bd172e..df690ef 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/service/CommentService.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/service/CommentService.java @@ -1,4 +1,145 @@ package com.TeamNull.LostArk.LostArk.service; + +import com.TeamNull.LostArk.LostArk.dto.CommentDto; +import com.TeamNull.LostArk.LostArk.entity.Comment; +import com.TeamNull.LostArk.LostArk.entity.User; +import com.TeamNull.LostArk.LostArk.repository.CommentRepository; +import com.TeamNull.LostArk.LostArk.repository.ResultRepository; +import com.TeamNull.LostArk.LostArk.repository.UserRepository; +import lombok.RequiredArgsConstructor; + +import org.springframework.data.domain.*; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestParam; + + +import java.sql.Timestamp; +import java.util.*; +import java.util.stream.Collectors; + + +@Service +@RequiredArgsConstructor public class CommentService { + + private final CommentRepository commentRepository; + private final ResultRepository resultRepository; + private final UserRepository userRepository; + + public Map getComments(int page, String searchText, Pageable pageable) { + Page comments; + + if (searchText != null && !searchText.isEmpty()) { + comments = getCommentSearch(searchText, PageRequest.of(page - 1, pageable.getPageSize(), pageable.getSort())); + } else { + comments = commentRepository.findAll(PageRequest.of(page - 1, pageable.getPageSize(), pageable.getSort())) + .map(comment -> new CommentDto.CommentResponseDto( + comment.getId(), + comment.getCreatedAt(), + comment.getContent(), + comment.getUser().getId(), + comment.getTopFactorResult(), + comment.getNickName() + )); + } + + int totalPage = comments.getTotalPages(); + int currentPage = comments.getNumber() + 1; + int startPage = Math.max(1, currentPage - 2); + int endPage = Math.min(totalPage, startPage + 4); + + Map response = new HashMap<>(); + response.put("comments", comments.getContent()); + response.put("totalPages", totalPage); + response.put("currentPage", currentPage); + response.put("startPage", startPage); + response.put("endPage", endPage); + + return response; + + } + + public void getAddComment( String content, + String password, + String nickname, + UUID userID + ) + { + Comment comment = new Comment(); + + comment.setNickName(nickname); + comment.setPassword(password); + comment.setContent(content); + String topFactorResult = resultRepository.findByUserId(userID) + .map(result -> result.getTopFactor1().getJobName()) + .orElseThrow(()-> new IllegalArgumentException("사용자의 정보가 존재하지 않습니다.." + userID)); + comment.setTopFactorResult(topFactorResult); + + User user = userRepository.findById(userID) + .orElseThrow(()-> new IllegalArgumentException("사용자의 아이디가 존재하지 않습니다..")); + comment.setUser(user); + + + commentRepository.save(comment); + } + + public ResponseEntity getCommentDelete(UUID userID, + Integer commentId, + String password){ + if (password == null || password.isEmpty()) { + return ResponseEntity.badRequest().body("비밀번호가 없습니다."); + } + + return commentRepository.findByUserIdAndId(userID, commentId) + .filter(comment -> comment.getPassword().equals(password)) + .map(comment -> { + commentRepository.deleteById(commentId); + return ResponseEntity.ok("댓글이 성공적으로 삭제되었습니다"); + }).orElseGet(()-> + ResponseEntity.status(HttpStatus.NOT_FOUND) + .body("해당 사용자가 작성한 댓글을 찾을 수 없습니다.")); + } + + + public ResponseEntity getCommentUpdate(UUID userID, Integer commentId, String password, String content) { + if (password == null || password.isEmpty()) { + return ResponseEntity.badRequest().body("비밀번호를 입력해주세요."); + } + return commentRepository.findByUserIdAndId(userID, commentId) + .filter(comment -> comment.getPassword().equals(password)) + .map(comment -> { + comment.setContent(content); // 댓글 내용 업데이트 + commentRepository.save(comment); // 업데이트된 댓글 저장 + return ResponseEntity.ok("댓글이 성공적으로 수정되었습니다."); + }) + .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND) + .body("해당 사용자의 댓글을 찾을 수 없습니다.")); + } + + public Page getCommentSearch(String searchText, Pageable pageable) { + List search = commentRepository.findByTopFactorResultContainingIgnoreCase(searchText); + + // 검색 결과를 페이지네이션 형태로 변환 + List responseDtoList = search.stream() + .map(comment -> new CommentDto.CommentResponseDto( + comment.getId(), + comment.getCreatedAt(), + comment.getContent(), + comment.getUser().getId(), + comment.getTopFactorResult(), + comment.getNickName() + )).toList(); + + // 페이지네이션 처리 + int start = (int) pageable.getOffset(); + int end = Math.min((start + pageable.getPageSize()), responseDtoList.size()); + + return new PageImpl<>(responseDtoList.subList(start, end), pageable, responseDtoList.size()); + + } } + diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/service/DataService.java b/src/main/java/com/TeamNull/LostArk/LostArk/service/DataService.java index 7b0898c..ab5c827 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/service/DataService.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/service/DataService.java @@ -1,4 +1,65 @@ package com.TeamNull.LostArk.LostArk.service; +import com.TeamNull.LostArk.LostArk.Job.JobAttributes; +import com.TeamNull.LostArk.LostArk.dto.DataDto; +import com.TeamNull.LostArk.LostArk.entity.Data; +import com.TeamNull.LostArk.LostArk.repository.DataRepository; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor public class DataService { -} + + private final DataRepository dataRepository; + private final ObjectMapper objectMapper = new ObjectMapper(); + + // json 파일을 읽어와서 DB에 저장하는 메소드 + private Map loadJobAttributesFromJson() { + try { + InputStream inputStream = getClass().getResourceAsStream("/LostArk.json"); + return objectMapper.readValue(inputStream, new TypeReference>() {}); + } catch (Exception e) { + throw new RuntimeException("json 파일에서 불러오기 실패", e); + } + } + private final Map jobAttributesMap = loadJobAttributesFromJson(); + + + public List read() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Optional data = dataRepository.findById(1); + List jobNames = new ArrayList<>(jobAttributesMap.keySet()); + + if(data.isPresent()){ + Data result = data.get(); + List resData = new ArrayList<>(); + for(String jobName : jobNames){ + String methodName = "get" + jobName.substring(0, 1).toUpperCase() + jobName.substring(1); + JobAttributes jobAttributes = jobAttributesMap.get(jobName); + try { + Method method = result.getClass().getMethod(methodName); + int value = (int) method.invoke(result); + resData.add(new DataDto(jobAttributes.getJobName(), value, jobAttributes.getIcon())); + } catch (Exception e) { + e.printStackTrace(); + } + } + + resData = resData.stream() + .sorted(Comparator.comparingDouble(DataDto::getValue).reversed()) + .collect(Collectors.toList()); + return resData; + + } + + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/service/OuterDataService.java b/src/main/java/com/TeamNull/LostArk/LostArk/service/OuterDataService.java index 525628d..eab230f 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/service/OuterDataService.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/service/OuterDataService.java @@ -1,4 +1,142 @@ package com.TeamNull.LostArk.LostArk.service; +import com.TeamNull.LostArk.LostArk.Job.JobAttributes; +import com.TeamNull.LostArk.LostArk.Job.TopFactor; +import com.TeamNull.LostArk.LostArk.dto.OuterDataDto; +import com.TeamNull.LostArk.LostArk.entity.OuterData; +import com.TeamNull.LostArk.LostArk.repository.OuterDataRepository; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.sql.Timestamp; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor public class OuterDataService { + + private final OuterDataRepository outerDataRepository; + private final ObjectMapper objectMapper = new ObjectMapper(); + + // json 파일을 읽어와서 DB에 저장하는 메소드 + private Map loadJobAttributesFromJson() { + try { + InputStream inputStream = getClass().getResourceAsStream("/LostArk.json"); + return objectMapper.readValue(inputStream, new TypeReference>() {}); + } catch (Exception e) { + throw new RuntimeException("json 파일에서 불러오기 실패", e); + } + } + + private final Map jobAttributesMap = loadJobAttributesFromJson(); + + + // 매일 00시에 실행 + @Scheduled(cron = "0 0 0 * * *") + public void update() throws IOException, ParseException { + create(); + } + + // outerData DB에 저장 + public void create() throws IOException, ParseException { + String result; + URL url = new URL("https://asia-northeast3-loasearch.cloudfunctions.net/app/total?minLv=&maxLv="); + BufferedReader bf; + bf = new BufferedReader(new InputStreamReader(url.openStream(),"UTF-8")); + result = bf.readLine(); + + // 받아온 데이터를 JSON으로 변환 + JSONParser jsonParser = new JSONParser(); + JSONObject jsonObject = (JSONObject)jsonParser.parse(result); + JSONObject data = (JSONObject) jsonObject.get("data"); + JSONArray classTotalArr = (JSONArray)data.get("classTotal"); + + for (int i = 0; i < classTotalArr.size(); i++) { + JSONObject classData = (JSONObject) classTotalArr.get(i); + String className = (String) classData.get("className"); + + // 특정 클래스 이름이 있는 경우 해당 객체를 삭제 + if (className.equals("전사(남)") || className.equals("전사(여)") || + className.equals("무도가(남)") || className.equals("무도가(여)") || + className.equals("헌터(남)") || className.equals("헌터(여)") || + className.equals("마법사") || className.equals("암살자") || + className.equals("스페셜리스트")) { + + classTotalArr.remove(i); + i--; // remove 했으므로 인덱스 조정 + } + } + + // 받아온 JSON 배열을 Dto에 넣어서 save + OuterData outerData = new OuterData(); + for (Object obj : classTotalArr){ + JSONObject classData = (JSONObject) obj; + String className = (String) classData.get("className"); + int classTotal = Integer.parseInt(classData.get("classTotal").toString()); + + // className과 고정 데이터의 jobName이 일치하는 key값 찾기 + String key = jobAttributesMap.entrySet().stream() + .filter(entry -> entry.getValue().getJobName().equals(className)) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + + if (key != null) { + try { + String methodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1); + Method method = outerData.getClass().getMethod(methodName, int.class); + method.invoke(outerData, classTotal); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + } + outerData.setCreatedAt(new Timestamp(System.currentTimeMillis())); + outerData.setId(1); + outerDataRepository.save(outerData); + } + + public List read() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Optional outerData = outerDataRepository.findById(1); + List jobNames = new ArrayList<>(jobAttributesMap.keySet()); + + if(outerData.isPresent()){ + OuterData result = outerData.get(); + List resData = new ArrayList<>(); + for(String jobName : jobNames){ + String methodName = "get" + jobName.substring(0, 1).toUpperCase() + jobName.substring(1); + JobAttributes jobAttributes = jobAttributesMap.get(jobName); + try { + Method method = result.getClass().getMethod(methodName); + int value = (int) method.invoke(result); + resData.add(new OuterDataDto(jobAttributes.getJobName(), value, jobAttributes.getIcon())); + } catch (Exception e) { + e.printStackTrace(); + } + } + + resData = resData.stream() + .sorted(Comparator.comparingDouble(OuterDataDto::getValue).reversed()) + .collect(Collectors.toList()); + return resData; + + } + + return null; + } } diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/service/ResultService.java b/src/main/java/com/TeamNull/LostArk/LostArk/service/ResultService.java index 01229af..83908fd 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/service/ResultService.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/service/ResultService.java @@ -1,4 +1,155 @@ package com.TeamNull.LostArk.LostArk.service; +import com.TeamNull.LostArk.LostArk.Job.JobAttributes; +import com.TeamNull.LostArk.LostArk.Job.TopFactor; +import com.TeamNull.LostArk.LostArk.dto.ResultDto; +import com.TeamNull.LostArk.LostArk.entity.Data; +import com.TeamNull.LostArk.LostArk.entity.Result; +import com.TeamNull.LostArk.LostArk.entity.User; +import com.TeamNull.LostArk.LostArk.repository.DataRepository; +import com.TeamNull.LostArk.LostArk.repository.ResultRepository; +import com.TeamNull.LostArk.LostArk.repository.UserRepository; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; + +@Service public class ResultService { -} + + private final ResultRepository resultRepository; + private final UserRepository userRepository; + private final DataRepository dataRepository; + private final ObjectMapper objectMapper = new ObjectMapper(); + + + public ResultService(ResultRepository resultRepository, UserRepository userRepository, DataRepository dataRepository) { + this.resultRepository = resultRepository; + this.userRepository = userRepository; + this.dataRepository = dataRepository; + } + + // json 파일을 읽어와서 DB에 저장하는 메소드 + private Map loadJobAttributesFromJson() { + try { + InputStream inputStream = getClass().getResourceAsStream("/LostArk.json"); + return objectMapper.readValue(inputStream, new TypeReference>() {}); + } catch (Exception e) { + throw new RuntimeException("json 파일에서 불러오기 실패", e); + } + } + private final Map jobAttributesMap = loadJobAttributesFromJson(); + + @Transactional + public void top5 (UUID id) { + User user = userRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("user not found")); +//테스트 항목 평균값 계산 + double avg1 = (user.getQuestion1() + user.getQuestion2()) / 2.0; + double avg2 = (user.getQuestion3() + user.getQuestion4()) / 2.0; + double avg3 = (user.getQuestion5() + user.getQuestion6()) / 2.0; + double avg4 = (user.getQuestion7() + user.getQuestion8()) / 2.0; + double avg5 = (user.getQuestion9() + user.getQuestion10()) / 2.0; + +//각 성향별 점수 책정 + Map jobScores = jobAttributesMap.values().stream() + .collect(Collectors.toMap( + job -> job, + job -> avg1 * job.getAgreeableness() + + avg2 * job.getConscientiousness() + + avg3 * job.getExtraversion() + + avg4 * job.getOpenness() + + avg5 * job.getNeuroticism() + )); + + // 상위 5개 직업 선별 + + List top5Jobs = jobScores.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(5) + .map(entry -> new TopFactor(entry.getKey().getJobName(), entry.getValue())) + .collect(Collectors.toList()); + + + + + Result result = new Result(); + result.setUser(user); +//1위 부터 5위까지 저장 + result.setTopFactor1(top5Jobs.size() > 0 ? new TopFactor(top5Jobs.get(0).getJobName(), top5Jobs.get(0).getValue()) : null); + result.setTopFactor2(top5Jobs.size() > 1 ? new TopFactor(top5Jobs.get(1).getJobName(), top5Jobs.get(1).getValue()) : null); + result.setTopFactor3(top5Jobs.size() > 2 ? new TopFactor(top5Jobs.get(2).getJobName(), top5Jobs.get(2).getValue()) : null); + result.setTopFactor4(top5Jobs.size() > 3 ? new TopFactor(top5Jobs.get(3).getJobName(), top5Jobs.get(3).getValue()) : null); + result.setTopFactor5(top5Jobs.size() > 4 ? new TopFactor(top5Jobs.get(4).getJobName(), top5Jobs.get(4).getValue()) : null); + + resultRepository.save(result); + +//Data 테이블 매핑 + Data data = dataRepository.findById(1).orElseThrow(()-> new IllegalArgumentException("Data not found")); +//저장될때마다 createdAt 갱신 + data.setCreatedAt(Timestamp.from(Instant.now())); +//1위 직업 카운트 1 증가 + String className = result.getTopFactor1().getJobName(); + String key = jobAttributesMap.entrySet().stream() + .filter(entry -> entry.getValue().getJobName().equals(className)) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + + if (key != null) { + try { + String getMethodName = "get" + key.substring(0, 1).toUpperCase() + key.substring(1); + Method getMethod = data.getClass().getMethod(getMethodName); + int currentValue = (int) getMethod.invoke(data); + + String setMethodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1); + Method setMethod = data.getClass().getMethod(setMethodName, int.class); + setMethod.invoke(data, currentValue + 1); + + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + dataRepository.save(data); + + } + + // user UUID로 결과 반환 + public List getResult(UUID id) { + Result result = resultRepository.findByUserId(id).orElseThrow(() -> new IllegalArgumentException("result not found")); + List resultDtoList = new ArrayList<>(); + + for (int i = 1; i <= 5; i++) { + try { + Method getTopFactorMethod = result.getClass().getMethod("getTopFactor" + i); + TopFactor topFactor = (TopFactor) getTopFactorMethod.invoke(result); + String jobName = topFactor.getJobName(); + if (topFactor != null) { + String key = jobAttributesMap.entrySet().stream() + .filter(entry -> entry.getValue().getJobName().equals(jobName)) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + JobAttributes jobAttribute = jobAttributesMap.get(key); + if (jobAttribute != null) { + resultDtoList.add(new ResultDto(topFactor.getJobName(), topFactor.getValue(), + jobAttribute.getIcon(), jobAttribute.getColor())); + } + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + return resultDtoList; + } +} \ No newline at end of file diff --git a/src/main/java/com/TeamNull/LostArk/LostArk/service/UserService.java b/src/main/java/com/TeamNull/LostArk/LostArk/service/UserService.java index 0e70ccd..fc95490 100644 --- a/src/main/java/com/TeamNull/LostArk/LostArk/service/UserService.java +++ b/src/main/java/com/TeamNull/LostArk/LostArk/service/UserService.java @@ -1,4 +1,34 @@ package com.TeamNull.LostArk.LostArk.service; + +import com.TeamNull.LostArk.LostArk.dto.UserDto; +import com.TeamNull.LostArk.LostArk.entity.User; +import com.TeamNull.LostArk.LostArk.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor public class UserService { + + private final UserRepository userRepository; + + public User saveUser(UserDto userDto){ + + User user = new User(); + user.setQuestion1(userDto.getQuestion1()); + user.setQuestion2(userDto.getQuestion2()); + user.setQuestion3(userDto.getQuestion3()); + user.setQuestion4(userDto.getQuestion4()); + user.setQuestion5(userDto.getQuestion5()); + user.setQuestion6(userDto.getQuestion6()); + user.setQuestion7(userDto.getQuestion7()); + user.setQuestion8(userDto.getQuestion8()); + user.setQuestion9(userDto.getQuestion9()); + user.setQuestion10(userDto.getQuestion10()); + + return userRepository.save(user); + } + + } diff --git a/src/main/resources/LostArk.json b/src/main/resources/LostArk.json new file mode 100644 index 0000000..3a785a1 --- /dev/null +++ b/src/main/resources/LostArk.json @@ -0,0 +1,262 @@ +{ + "destroyer": { + "jobName": "디스트로이어", + "Agreeableness": "3", + "conscientiousness": "4", + "extraversion": "1", + "openness": "2", + "neuroticism": "5", + "color": "bg-sky-300", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/destroyer.png" + }, + "berserker": { + "jobName": "버서커", + "Agreeableness": "2", + "conscientiousness": "4", + "extraversion": "2", + "openness": "2", + "neuroticism": "5", + "color": "bg-red-700", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/berserker.png" + }, + "slayer": { + "jobName": "슬레이어", + "Agreeableness": "2", + "conscientiousness": "3", + "extraversion": "2", + "openness": "3", + "neuroticism": "5", + "color": "bg-red-400", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/berserker_female.png" + }, + "gunlancer": { + "jobName": "워로드", + "Agreeableness": "5", + "conscientiousness": "4", + "extraversion": "4", + "openness": "1", + "neuroticism": "1", + "color": "bg-cyan-200", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/warlord.png" + }, + "paladin": { + "jobName": "홀리나이트", + "Agreeableness": "3", + "conscientiousness": "4", + "extraversion": "5", + "openness": "1", + "neuroticism": "2", + "color": "bg-yellow-300", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/holyknight.png" + }, + "soulfist": { + "jobName": "기공사", + "Agreeableness": "2", + "conscientiousness": "2", + "extraversion": "5", + "openness": "3", + "neuroticism": "3", + "color": "bg-blue-400", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/soulmaster.png" + }, + "wardancer": { + "jobName": "배틀마스터", + "Agreeableness": "4", + "conscientiousness": "3", + "extraversion": "4", + "openness": "3", + "neuroticism": "1", + "color": "bg-amber-500", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/battlemaster.png" + }, + "breaker": { + "jobName": "브레이커", + "Agreeableness": "2", + "conscientiousness": "2", + "extraversion": "4", + "openness": "4", + "neuroticism": "3", + "color": "bg-amber-600", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/infighter_male.png" + }, + "striker": { + "jobName": "스트라이커", + "Agreeableness": "2", + "conscientiousness": "2", + "extraversion": "4", + "openness": "5", + "neuroticism": "2", + "color": "bg-amber-700", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/striker.png" + }, + "scrapper": { + "jobName": "인파이터", + "Agreeableness": "3", + "conscientiousness": "3", + "extraversion": "3", + "openness": "3", + "neuroticism": "3", + "color": "bg-amber-800", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/infighter.png" + }, + "glaivier": { + "jobName": "창술사", + "Agreeableness": "2", + "conscientiousness": "1", + "extraversion": "5", + "openness": "3", + "neuroticism": "4", + "color": "bg-red-800", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/lancemaster.png" + }, + "gunslinger": { + "jobName": "건슬링어", + "Agreeableness": "1", + "conscientiousness": "3", + "extraversion": "2", + "openness": "4", + "neuroticism": "5", + "color": "bg-red-900", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/gunslinger.png" + }, + "deadeye": { + "jobName": "데빌헌터", + "Agreeableness": "1", + "conscientiousness": "3", + "extraversion": "1", + "openness": "5", + "neuroticism": "5", + "color": "bg-cyan-800", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/devilhunter.png" + }, + "artillerist": { + "jobName": "블래스터", + "Agreeableness": "2", + "conscientiousness": "4", + "extraversion": "3", + "openness": "3", + "neuroticism": "3", + "color": "bg-cyan-950", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/blaster.png" + }, + "machinist": { + "jobName": "스카우터", + "Agreeableness": "2", + "conscientiousness": "4", + "extraversion": "4", + "openness": "2", + "neuroticism": "3", + "color": "bg-indigo-600", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/scouter.png" + }, + "sharpshooter": { + "jobName": "호크아이", + "Agreeableness": "2", + "conscientiousness": "3", + "extraversion": "3", + "openness": "4", + "neuroticism": "3", + "color": "bg-blue-700", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/hawkeye.png" + }, + "bard": { + "jobName": "바드", + "Agreeableness": "4", + "conscientiousness": "5", + "extraversion": "4", + "openness": "1", + "neuroticism": "1", + "color": "bg-amber-200", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/bard.png" + }, + "summoner": { + "jobName": "서머너", + "Agreeableness": "3", + "conscientiousness": "3", + "extraversion": "3", + "openness": "4", + "neuroticism": "2", + "color": "bg-blue-600", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/summoner.png" + }, + "sorceress": { + "jobName": "소서리스", + "Agreeableness": "2", + "conscientiousness": "3", + "extraversion": "2", + "openness": "5", + "neuroticism": "3", + "color": "bg-rose-600", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/sorceress.png" + }, + "arcanist": { + "jobName": "아르카나", + "Agreeableness": "2", + "conscientiousness": "3", + "extraversion": "3", + "openness": "5", + "neuroticism": "2", + "color": "bg-fuchsia-700", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/arcana.png" + }, + "shadowhunter": { + "jobName": "데모닉", + "Agreeableness": "3", + "conscientiousness": "3", + "extraversion": "2", + "openness": "2", + "neuroticism": "5", + "color": "bg-red-600", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/demonic.png" + }, + "reaper": { + "jobName": "리퍼", + "Agreeableness": "2", + "conscientiousness": "2", + "extraversion": "1", + "openness": "5", + "neuroticism": "5", + "color": "bg-green-600", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/reaper.png" + }, + "deathblade": { + "jobName": "블레이드", + "Agreeableness": "2", + "conscientiousness": "2", + "extraversion": "3", + "openness": "4", + "neuroticism": "4", + "color": "bg-sky-300", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/blade.png" + }, + "souleater": { + "jobName": "소울이터", + "Agreeableness": "1", + "conscientiousness": "1", + "extraversion": "5", + "openness": "5", + "neuroticism": "3", + "color": "bg-purple-800", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/soul_eater_s.png" + }, + "aeromancer": { + "jobName": "기상술사", + "Agreeableness": "4", + "conscientiousness": "3", + "extraversion": "4", + "openness": "2", + "neuroticism": "2", + "color": "bg-rose-300", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/weather_artist.png" + }, + "artist": { + "jobName": "도화가", + "Agreeableness": "4", + "conscientiousness": "4", + "extraversion": "4", + "openness": "2", + "neuroticism": "1", + "color": "bg-rose-200", + "icon": "https://cdn-lostark.game.onstove.com/2018/obt/assets/images/common/thumb/artist.png" + } +} \ No newline at end of file diff --git a/src/test/java/com/TeamNull/LostArk/LostArk/LostArkApplicationTests.java b/src/test/java/com/TeamNull/LostArk/LostArk/LostArkApplicationTests.java index c636649..1ee01b8 100644 --- a/src/test/java/com/TeamNull/LostArk/LostArk/LostArkApplicationTests.java +++ b/src/test/java/com/TeamNull/LostArk/LostArk/LostArkApplicationTests.java @@ -1,13 +1,72 @@ package com.TeamNull.LostArk.LostArk; +import com.TeamNull.LostArk.LostArk.controller.CommentController; +import com.TeamNull.LostArk.LostArk.controller.DataController; +import com.TeamNull.LostArk.LostArk.controller.OuterDataController; +import com.TeamNull.LostArk.LostArk.controller.ResultController; +import com.TeamNull.LostArk.LostArk.dto.DataDto; +import com.TeamNull.LostArk.LostArk.dto.OuterDataDto; +import com.TeamNull.LostArk.LostArk.dto.ResultDto; +import org.json.simple.parser.ParseException; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; @SpringBootTest class LostArkApplicationTests { - @Test - void contextLoads() { + private OuterDataController outerDataController; + private DataController dataController; + private ResultController resultController; + private CommentController commentController; + + @Autowired + public LostArkApplicationTests(OuterDataController outerDataController, DataController dataController, ResultController resultController, CommentController commentController) { + super(); + this.outerDataController = outerDataController; + this.dataController = dataController; + this.resultController = resultController; + this.commentController = commentController; } +// @Test +// void testAlluser() { +// List outerDataDtoList = this.outerDataController.alluser(); +// assertEquals(26, outerDataDtoList.size()); +// assertEquals("도화가", outerDataDtoList.get(0).getName()); +// System.out.println(outerDataDtoList.get(0).getName()); +// System.out.println(outerDataDtoList.get(0).getValue()); +// System.out.println(outerDataDtoList.get(0).getIcon()); +// } + +// @Test +// void testData() { +// List dataDtoList = this.dataController.read(); +// assertEquals(26, dataDtoList.size()); +// System.out.println(dataDtoList.get(0).getName()); +// System.out.println(dataDtoList.get(0).getValue()); +// System.out.println(dataDtoList.get(0).getIcon()); +// } + +// @Test +// void testTop5() { +// this.resultController.top5(UUID.fromString("f9ceff67-e36b-43d9-89a8-263a334a6313")); +// } + +// @Test +// void testResult() { +// ResponseEntity> result = this.resultController.result(UUID.fromString("ffb057b9-0b82-4789-8440-ded61c248cd2")); +// System.out.println(result.getBody().get(0).getName()); +// } + }