Skip to content

Commit

Permalink
Merge pull request #6 from DEPthes/develop
Browse files Browse the repository at this point in the history
[DEPLOY]
  • Loading branch information
jisujeong0 authored Jun 28, 2024
2 parents 6ccfad0 + 3404523 commit 7228177
Show file tree
Hide file tree
Showing 27 changed files with 828 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ jobs:
run: |
mkdir -p src/main/resources
echo "${{ secrets.APPLICATION_DATABASE_YML }}" | base64 --decode > src/main/resources/application-database.yml
echo "${{ secrets.APPLICATION_S3_YML }}" | base64 --decode > src/main/resources/application-s3.yml
echo "${{ secrets.APPLICATION_GPT_YML }}" | base64 --decode > src/main/resources/application-gpt.yml
# (4) gradlew에 실행 권한 부여
- name: Grant execute permission for gradlew
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ out/

### YML ###
/src/main/resources/application-database.yml
/src/main/resources/application-gpt.yml
/src/main/resources/application-s3.yml
2 changes: 2 additions & 0 deletions src/main/java/depth/hackerthon/team3/Team3Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

@SpringBootApplication
@PropertySource(value = { "classpath:application-database.yml" }, factory = YamlPropertySourceFactory.class)
@PropertySource(value = { "classpath:application-gpt.yml" }, factory = YamlPropertySourceFactory.class)
@PropertySource(value = { "classpath:application-s3.yml" }, factory = YamlPropertySourceFactory.class)
public class Team3Application {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package depth.hackerthon.team3.domain.board.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import depth.hackerthon.team3.domain.board.domain.Board;
import depth.hackerthon.team3.domain.board.dto.GenerateShavedIceReq;
import depth.hackerthon.team3.domain.board.dto.RegisterMyBoardReq;
import depth.hackerthon.team3.domain.board.service.BoardService;
import depth.hackerthon.team3.domain.gpt.service.GptService;
import depth.hackerthon.team3.domain.s3.service.S3Uploader;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Optional;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/board")
@Tag(name = "Board", description = "게시판 관련 API입니다.")
public class BoardController {

private final BoardService boardService;
private final GptService gptService;
private final S3Uploader s3Uploader;

@Operation(summary = "빙수의 전당 목록 조회", description = "모든 빙수 게시물을 조회합니다.")
@GetMapping("/all")
public List<Board> getAllBoards() {
return boardService.getAllBoards();
}
@Operation(summary = "빙수 상세 조회", description = "특정 ID의 빙수 게시물을 조회합니다.")
@GetMapping("/{boardId}")
public Optional<Board> getBoardById(@PathVariable("boardId") Long boardId) {
return boardService.getBoardById(boardId);
}
@Operation(summary = "빙수 정렬 조회", description = "특정 기준으로 빙수 게시물을 정렬하여 조회합니다.")
@GetMapping("/sorted")
public List<Board> getBoardsSorted(@RequestParam String sortBy) {
return boardService.getBoardsSorted(sortBy);
}

@PostMapping()
public ResponseEntity<?> postMyBoard(@RequestPart MultipartFile image, @Valid @RequestPart RegisterMyBoardReq registerMyBoardReq) {
return boardService.registerMyBoard(image, registerMyBoardReq);
}

@GetMapping("/generate")
public ResponseEntity<?> generateImage(@RequestBody GenerateShavedIceReq generateShavedIceReq) throws JsonProcessingException {
String prompt = generateShavedIceReq.getItem1() + ", " + generateShavedIceReq.getItem2() + ", " + generateShavedIceReq.getItem3();
return gptService.getImageFromDallE(prompt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package depth.hackerthon.team3.domain.board.domain;

import depth.hackerthon.team3.domain.common.BaseEntity;
import depth.hackerthon.team3.domain.user.domain.User;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor
@Getter
public class Board extends BaseEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long boardId;//아이디
private String boardPassword;//비번
private String image;
private String shavedIceName; //빙수이름
private String description; //빙수설명
private int reaction1; //환상적이에요
private int reaction2; //웃겨요
private int reaction3; //먹고싶어요
private int reaction4; //이상해요
private int reaction5; //별로예요
private String item1; //재료1
private String item2; //재료2
private String item3; //재료3

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="user_id")
private User user;

@Builder
public Board(User user, String boardPassword, String image, String shavedIceName, String description, String item1, String item2, String item3) {
this.user = user;
this.boardPassword = boardPassword;
this.description = description;
this.image = image;
this.reaction1 = 0;
this.reaction2 = 0;
this.reaction3 = 0;
this.reaction4 = 0;
this.reaction5 = 0;
this.shavedIceName = shavedIceName;
this.item1 = item1;
this.item2 = item2;
this.item3 = item3;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package depth.hackerthon.team3.domain.board.domain.repository;

import depth.hackerthon.team3.domain.board.domain.Board;
import depth.hackerthon.team3.domain.user.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface BoardRepository extends JpaRepository<Board, Long> {
List<Board> findByUser(User user);

Board findByBoardId(Long boardId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package depth.hackerthon.team3.domain.board.dto;

import lombok.Getter;

@Getter
public class GenerateShavedIceReq {

private String item1;
private String item2;
private String item3;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package depth.hackerthon.team3.domain.board.dto;

import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;

@Getter
public class RegisterMyBoardReq {

@Pattern(regexp = "^(?=.*[a-zA-Z])?(?=.*\\d)[a-zA-Z\\d]{4,8}$", message = "비밀번호 형식은 숫자 또는 숫자, 영문 조합 최소 4자 최대 8자입니다.")
private String boardPassword;//비번

@Size(min = 2, max = 12, message = "제목은 2~12자 사이여야 합니다.")
private String shavedIceName; //빙수이름

@Size(min = 2, max = 90, message = "내용은 2~90자 사이여야 합니다.")
private String description; //빙수설명
private String item1; //재료1
private String item2; //재료2
private String item3; //재료3
private String deviceId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package depth.hackerthon.team3.domain.board.service;


import depth.hackerthon.team3.domain.board.domain.Board;
import depth.hackerthon.team3.domain.board.domain.repository.BoardRepository;
import depth.hackerthon.team3.domain.board.dto.RegisterMyBoardReq;
import depth.hackerthon.team3.domain.s3.service.S3Uploader;
import depth.hackerthon.team3.domain.user.domain.User;
import depth.hackerthon.team3.domain.user.domain.repository.UserRepository;
import depth.hackerthon.team3.global.payload.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class BoardService {
private final BoardRepository boardRepository;
private final UserRepository userRepository;
private final S3Uploader s3Uploader;
// 빙수 목록 조회 메서드
public List<Board> getAllBoards() {
return boardRepository.findAll();
}
// 빙수 상세 조회 메서드
public Optional<Board> getBoardById(Long boardId) {
return boardRepository.findById(boardId);
}
// 빙수 정렬 조회 메서드
public List<Board> getBoardsSorted(String sortBy) {
return boardRepository.findAll(Sort.by(Sort.Direction.DESC, sortBy));
}

// 내 게시글 작성
@Transactional
public ResponseEntity<?> registerMyBoard(MultipartFile image, RegisterMyBoardReq registerMyBoardReq) {
User user = userRepository.findByDeviceId(registerMyBoardReq.getDeviceId());
String imageUrl = s3Uploader.uploadImage(image);
Board board = Board.builder()
.user(user)
.shavedIceName(registerMyBoardReq.getShavedIceName())
.description(registerMyBoardReq.getDescription())
.boardPassword(registerMyBoardReq.getBoardPassword())
.image(imageUrl)
.item1(registerMyBoardReq.getItem1())
.item2(registerMyBoardReq.getItem2())
.item3(registerMyBoardReq.getItem3())
.build();
boardRepository.save(board);

ApiResponse apiResponse = ApiResponse.builder()
.check(true)
.information("내 빙수가 등록되었습니다.")
.build();
return ResponseEntity.ok(apiResponse);
}
}
22 changes: 22 additions & 0 deletions src/main/java/depth/hackerthon/team3/domain/common/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package depth.hackerthon.team3.domain.common;

import jakarta.persistence.*;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
@CreatedDate
@Column(name = "created_at", updatable=false)
private LocalDateTime createdAt;

@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package depth.hackerthon.team3.domain.gpt.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import depth.hackerthon.team3.global.config.GptConfig;
import depth.hackerthon.team3.global.payload.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;

@Service
@RequiredArgsConstructor
public class GptService {

@Value("${chatgpt.api-key}")
private String apiKey;

private static final String DALL_E_URL = "https://api.openai.com/v1/images/generations";

// DALL-E 이미지 요청 메서드 추가
public JsonNode callDallE(String prompt) throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(apiKey);

ObjectMapper objectMapper = new ObjectMapper();

Map<String, Object> bodyMap = new HashMap<>();
bodyMap.put("prompt", GptConfig.CONTENT + prompt);
bodyMap.put("num_images", 1);

String body = objectMapper.writeValueAsString(bodyMap);

HttpEntity<String> request = new HttpEntity<>(body, headers);

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(DALL_E_URL, HttpMethod.POST, request, String.class);

return objectMapper.readTree(response.getBody());
}

public ResponseEntity<?> getImageFromDallE(String prompt) throws JsonProcessingException {
JsonNode jsonNode = callDallE(prompt);
String imageUrl = jsonNode.path("data").get(0).path("url").asText();

ApiResponse apiResponse = ApiResponse.builder()
.check(true)
.information(imageUrl)
.build();
return ResponseEntity.ok(apiResponse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package depth.hackerthon.team3.domain.randomIngredient.controller;

import depth.hackerthon.team3.domain.randomIngredient.service.RandomIngredientService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/ingredient")
@Tag(name = "Random Ingredient", description = "재료 관련 API입니다.")
public class RandomIngredientController {

private final RandomIngredientService randomIngredientService;
// 랜덤 재료 조회
@GetMapping
public ResponseEntity<?> getRandomIngredients() {
return randomIngredientService.getRandomIngredients();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package depth.hackerthon.team3.domain.randomIngredient.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor
@Getter
public class RandomIngredient {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long ingredientId; //재료아이디
private String ingredientName; //재료이름

@Builder
public RandomIngredient(String ingredientName) {
this.ingredientName = ingredientName;
}

}

Loading

0 comments on commit 7228177

Please sign in to comment.