diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d460640..a4ef525 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -31,9 +31,10 @@ jobs: run: | docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} docker build -t ${{ secrets.DOCKER_REPO }}/mappin-server . - docker push ${{ secrets.DOCKER_REPO }}/mappin-server - docker build -t ${{ secrets.DOCKER_REPO }}/mappin-nginx . - docker push ${{ secrets.DOCKER_REPO }}/mappin-nginx + docker push ${{ secrets.DOCKER_REPO }}/mappin-server + + docker build -t ${{ secrets.DOCKER_REPO }}/mappin-nginx -f dockerfile-nginx . + docker push ${{ secrets.DOCKER_REPO }}/mappin-nginx - name: Install Docker on EC2 uses: appleboy/ssh-action@v0.1.6 @@ -67,10 +68,10 @@ jobs: script: | if [ "$(sudo docker ps -aq)" ]; then sudo docker stop $(sudo docker ps -aq) - sudo docker rm $(sudo docker ps -aq) + sudo docker rm -f $(sudo docker ps -aq) fi if [ "$(sudo docker images -aq)" ]; then - sudo docker rmi $(sudo docker images -aq) + sudo docker rmi -f $(sudo docker images -aq) fi - name: Copy file to EC2 @@ -96,6 +97,9 @@ jobs: echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" >> ~/.env echo "KAKAO_CLIENT=${{ secrets.KAKAO_CLIENT }}" >> ~/.env echo "KAKAO_SECRET=${{ secrets.KAKAO_SECRET }}" >> ~/.env + echo "KAKAO_REST_API_KEY=${{ secrets.KAKAO_REST_API_KEY }}" >> ~/.env + echo "S3accessKey=${{ secrets.S3ACCESSKEY }}" >> ~/.env + echo "S3SecretKey=${{ secrets.S3SECRETKEY }}" >> ~/.env # Copy .env file to the project directory #cp ~/.env /home/ubuntu/.env diff --git a/build.gradle b/build.gradle index 783ef92..a50544a 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.1.RELEASE' compileOnly 'org.projectlombok:lombok' diff --git a/dockerfile-nginx b/dockerfile-nginx index e6888c3..6ec8e80 100644 --- a/dockerfile-nginx +++ b/dockerfile-nginx @@ -1,2 +1,3 @@ FROM nginx COPY ./nginx/conf.d/nginx.conf /etc/nginx/conf.d +CMD ["nginx" ,"-g", "daemon off;"] diff --git a/nginx/conf.d/nginx.conf b/nginx/conf.d/nginx.conf index ab6d967..e186d00 100644 --- a/nginx/conf.d/nginx.conf +++ b/nginx/conf.d/nginx.conf @@ -1,7 +1,10 @@ +upstream server { + server web:8080; +} + server { listen 80; server_name hohoseung.shop; - access_log logs/host.access.log; location / { proxy_pass http://web:8080; diff --git a/src/main/java/com/server/mappin/config/S3Config.java b/src/main/java/com/server/mappin/config/S3Config.java new file mode 100644 index 0000000..0c4778f --- /dev/null +++ b/src/main/java/com/server/mappin/config/S3Config.java @@ -0,0 +1,32 @@ +package com.server.mappin.config; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3Client amazonS3Client() { + BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey); + return (AmazonS3Client) AmazonS3ClientBuilder.standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(awsCreds)) + .build(); + } + +} diff --git a/src/main/java/com/server/mappin/controller/LostController.java b/src/main/java/com/server/mappin/controller/LostController.java index 644fbcf..7c692f6 100644 --- a/src/main/java/com/server/mappin/controller/LostController.java +++ b/src/main/java/com/server/mappin/controller/LostController.java @@ -1,8 +1,6 @@ package com.server.mappin.controller; -import com.server.mappin.dto.FindByCategoryResponseDto; -import com.server.mappin.dto.LostRegisterRequestDto; -import com.server.mappin.dto.LostRegisterResponseDto; +import com.server.mappin.dto.*; import com.server.mappin.service.LostService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -18,6 +16,7 @@ import org.springframework.web.bind.annotation.*; import javax.persistence.Column; +import java.io.IOException; import java.util.List; @Tag(name = "Lost API", description = "분실물 관련 API 명세서") @RestController @@ -29,7 +28,7 @@ public class LostController { @Operation(summary = "분실물 등록") @ApiResponse(content = @Content(schema = @Schema(implementation = LostRegisterResponseDto.class))) @PutMapping("/lost/register") - public ResponseEntity registerLost(@RequestBody LostRegisterRequestDto lostRegisterRequestDto, Authentication authentication){ + public ResponseEntity registerLost(@ModelAttribute LostRegisterRequestDto lostRegisterRequestDto, Authentication authentication) throws IOException { try{ LostRegisterResponseDto lostRegisterResponseDto = lostService.registerLost(lostRegisterRequestDto, authentication.getName()); return new ResponseEntity<>(lostRegisterResponseDto,HttpStatus.OK); @@ -39,12 +38,24 @@ public ResponseEntity registerLost(@RequestBody LostRegisterRequestDto lostRe } @Operation(summary = "카테고리 검색",description = "분실물을 카테고리 별로 검색") - @ApiResponse(content = @Content(schema = @Schema(implementation = FindByCategoryResponseDto.class))) + @ApiResponse(content = @Content(schema = @Schema(implementation = FindByCategoryListResponseDto.class))) @GetMapping("/search/category/{category_name}") public ResponseEntity searchByCategory(@RequestParam(value = "category_name") String name){ try{ - List byCategory = lostService.findByCategory(name); - return new ResponseEntity<>(byCategory,HttpStatus.OK); + FindByCategoryListResponseDto findByCategoryListResponseDto = lostService.findByCategory(name); + return new ResponseEntity<>(findByCategoryListResponseDto,HttpStatus.OK); + }catch (IllegalStateException e){ + return new ResponseEntity<>("에러가 발생했습니다", HttpStatus.CONFLICT); + } + } + + @Operation(summary = "동 검색",description = "분실물을 동 별로 검색") + @ApiResponse(content = @Content(schema = @Schema(implementation = FindByDongResponseDto.class))) + @GetMapping("/search/dong/{dong_name}") + public ResponseEntity searchByDong(@RequestParam(value = "dong_name") String dongName){ + try{ + List byDong = lostService.findByDong(dongName); + return new ResponseEntity<>(byDong,HttpStatus.OK); }catch (IllegalStateException e){ return new ResponseEntity<>("에러가 발생했습니다", HttpStatus.CONFLICT); } diff --git a/src/main/java/com/server/mappin/controller/MemberController.java b/src/main/java/com/server/mappin/controller/MemberController.java index 923f8ef..a651a3a 100644 --- a/src/main/java/com/server/mappin/controller/MemberController.java +++ b/src/main/java/com/server/mappin/controller/MemberController.java @@ -1,6 +1,8 @@ package com.server.mappin.controller; +import com.server.mappin.dto.AdminLoginResponseDto; import com.server.mappin.dto.LoginResponseDto; +import com.server.mappin.dto.UserLoginResponseDto; import com.server.mappin.dto.MemberLoginDto; import com.server.mappin.service.MemberService; import io.swagger.v3.oas.annotations.Operation; @@ -25,14 +27,15 @@ public class MemberController { private final MemberService memberService; @Operation(summary = "로그인", description = "새로운 회원은 회원가입, 기존 회원은 로그인") - @ApiResponses({ - @ApiResponse(content = @Content(schema = @Schema(implementation = LoginResponseDto.class))) + @ApiResponses(value = { + @ApiResponse(responseCode ="200",description ="일반 사용자 로그인", content = @Content(schema = @Schema(implementation = UserLoginResponseDto.class))), + @ApiResponse(responseCode = "201",description = "가게 주인 로그인", content = @Content(schema = @Schema(implementation = AdminLoginResponseDto.class))) }) @PostMapping("/login") - public ResponseEntity create(@RequestBody MemberLoginDto memberCreateDto){ + public ResponseEntity login(@RequestBody MemberLoginDto memberCreateDto){ try{ - LoginResponseDto loginResponseDto = memberService.create(memberCreateDto); - return new ResponseEntity<>(loginResponseDto,HttpStatus.OK); + LoginResponseDto responseDto = memberService.login(memberCreateDto); + return new ResponseEntity<>(responseDto,HttpStatus.OK); }catch (IllegalStateException e){ return new ResponseEntity<>("에러가 발생했습니다", HttpStatus.CONFLICT); } diff --git a/src/main/java/com/server/mappin/controller/PostController.java b/src/main/java/com/server/mappin/controller/PostController.java index 0b633cb..7ee2297 100644 --- a/src/main/java/com/server/mappin/controller/PostController.java +++ b/src/main/java/com/server/mappin/controller/PostController.java @@ -28,7 +28,8 @@ public class PostController { @Operation(summary = "게시물 작성",description = "게시물을 작성합니다") @ApiResponses({ - @ApiResponse(content = @Content(schema = @Schema(implementation = PostCreateResponseDto.class))) + @ApiResponse(responseCode ="200",description ="게시물 작성 성공",content = @Content(schema = @Schema(implementation = PostCreateResponseDto.class))), + @ApiResponse(responseCode ="400",description ="게시물 작성 실패",content = @Content(schema = @Schema(implementation = PostCreateResponseDto.class))) }) @PutMapping("/post") public ResponseEntity create(@RequestBody PostCreateRequestDto postCreateDto, Authentication authentication){ diff --git a/src/main/java/com/server/mappin/controller/S3Controller.java b/src/main/java/com/server/mappin/controller/S3Controller.java new file mode 100644 index 0000000..ecd32a3 --- /dev/null +++ b/src/main/java/com/server/mappin/controller/S3Controller.java @@ -0,0 +1,29 @@ +package com.server.mappin.controller; + +import com.server.mappin.service.S3Service; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@RequiredArgsConstructor +@RequestMapping(value = "aws-s3") +@RestController +public class S3Controller { + + private final S3Service s3UploadUtil; + + @PostMapping(name = "S3 파일 업로드", value = "/file") + public String fileUpload(@RequestParam("files") MultipartFile multipartFile) throws IOException { + s3UploadUtil.upload(multipartFile, "test"); // test 폴더에 파일 생성 + return "success"; + } + + @DeleteMapping(name = "S3 파일 삭제", value = "/file") + public String fileDelete(@RequestParam("path") String path) { + s3UploadUtil.delete(path); + return "success"; + } + +} diff --git a/src/main/java/com/server/mappin/domain/Location.java b/src/main/java/com/server/mappin/domain/Location.java index 0b3454a..ba4105e 100644 --- a/src/main/java/com/server/mappin/domain/Location.java +++ b/src/main/java/com/server/mappin/domain/Location.java @@ -10,6 +10,7 @@ @Entity @Table(name = "location") @NoArgsConstructor +@Getter public class Location { @Id diff --git a/src/main/java/com/server/mappin/domain/Post.java b/src/main/java/com/server/mappin/domain/Post.java index fb05866..8e37535 100644 --- a/src/main/java/com/server/mappin/domain/Post.java +++ b/src/main/java/com/server/mappin/domain/Post.java @@ -37,9 +37,9 @@ public class Post { private String imageUrl; - private Integer x; + private Double x; - private Integer y; + private Double y; @ManyToOne @JoinColumn(name = "location_id") @@ -50,7 +50,7 @@ public class Post { private Category category; @Builder - public Post(Member member, String title, String content, LocalDate createdAt, LocalDate lostDate, String imageUrl, Integer x, Integer y, Location location, Category category) { + public Post(Member member, String title, String content, LocalDate createdAt, LocalDate lostDate, String imageUrl, Double x, Double y, Location location, Category category) { this.member = member; this.title = title; this.content = content; diff --git a/src/main/java/com/server/mappin/domain/Shop.java b/src/main/java/com/server/mappin/domain/Shop.java index 536a0ba..543aee1 100644 --- a/src/main/java/com/server/mappin/domain/Shop.java +++ b/src/main/java/com/server/mappin/domain/Shop.java @@ -1,13 +1,16 @@ package com.server.mappin.domain; import lombok.Builder; +import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import javax.persistence.*; @Entity @Table(name = "shop") @NoArgsConstructor +@Getter @Setter public class Shop { @Id diff --git a/src/main/java/com/server/mappin/dto/AdminLoginResponseDto.java b/src/main/java/com/server/mappin/dto/AdminLoginResponseDto.java new file mode 100644 index 0000000..cf71d7a --- /dev/null +++ b/src/main/java/com/server/mappin/dto/AdminLoginResponseDto.java @@ -0,0 +1,55 @@ +package com.server.mappin.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import org.springframework.data.geo.Point; + +@Data +@Builder +@Getter +public class AdminLoginResponseDto implements LoginResponseDto { + private int statusCode; + private String isSuccess; + private Long id; + private String role; + private String jwt; + private String token_type; + private long expires_in; + private Point geo; + + @Override + public int getStatusCode(){ + return statusCode; + } + @Override + public String getIsSuccess() { + return isSuccess; + } + + @Override + public Long getId() { + return id; + } + + @Override + public String getRole() { + return role; + } + + @Override + public String getJwt() { + return jwt; + } + + @Override + public String getToken_Type() { + return token_type; + } + + @Override + public long getExpires_In() { + return expires_in; + } + +} diff --git a/src/main/java/com/server/mappin/dto/FindByCategoryListResponseDto.java b/src/main/java/com/server/mappin/dto/FindByCategoryListResponseDto.java new file mode 100644 index 0000000..5c39582 --- /dev/null +++ b/src/main/java/com/server/mappin/dto/FindByCategoryListResponseDto.java @@ -0,0 +1,14 @@ +package com.server.mappin.dto; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Builder +@Data +public class FindByCategoryListResponseDto { + private int statusCode; + private String isSuccess; + private List losts; +} diff --git a/src/main/java/com/server/mappin/dto/FindByCategoryResponseDto.java b/src/main/java/com/server/mappin/dto/FindByCategoryResponseDto.java index ee87155..31c1881 100644 --- a/src/main/java/com/server/mappin/dto/FindByCategoryResponseDto.java +++ b/src/main/java/com/server/mappin/dto/FindByCategoryResponseDto.java @@ -3,6 +3,8 @@ import lombok.Builder; import lombok.Data; +import java.util.List; + @Data @Builder public class FindByCategoryResponseDto { diff --git a/src/main/java/com/server/mappin/dto/FindByDongResponseDto.java b/src/main/java/com/server/mappin/dto/FindByDongResponseDto.java new file mode 100644 index 0000000..2dc4349 --- /dev/null +++ b/src/main/java/com/server/mappin/dto/FindByDongResponseDto.java @@ -0,0 +1,15 @@ +package com.server.mappin.dto; + +import lombok.Builder; +import lombok.Data; +import java.time.LocalDateTime; + +@Data +@Builder +public class FindByDongResponseDto { + private Long id; + private String title; + private LocalDateTime createdAt; + private String imageUrl; + private String dong; +} diff --git a/src/main/java/com/server/mappin/dto/LoginResponseDto.java b/src/main/java/com/server/mappin/dto/LoginResponseDto.java index ecf78f6..ff06ea3 100644 --- a/src/main/java/com/server/mappin/dto/LoginResponseDto.java +++ b/src/main/java/com/server/mappin/dto/LoginResponseDto.java @@ -1,14 +1,11 @@ package com.server.mappin.dto; -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class LoginResponseDto { - private String isSuccess; - private Long id; - private String jwt; - private String token_type; - private long expires_in; +public interface LoginResponseDto { + int getStatusCode(); + String getIsSuccess(); + Long getId(); + String getRole(); + String getJwt(); + String getToken_Type(); + long getExpires_In(); } diff --git a/src/main/java/com/server/mappin/dto/LostRegisterRequestDto.java b/src/main/java/com/server/mappin/dto/LostRegisterRequestDto.java index 5002717..b19f2ec 100644 --- a/src/main/java/com/server/mappin/dto/LostRegisterRequestDto.java +++ b/src/main/java/com/server/mappin/dto/LostRegisterRequestDto.java @@ -1,10 +1,12 @@ package com.server.mappin.dto; +import com.fasterxml.jackson.annotation.JsonFormat; import com.server.mappin.domain.Category; import com.server.mappin.domain.Location; import lombok.Builder; import lombok.Data; import org.springframework.data.geo.Point; +import org.springframework.web.multipart.MultipartFile; import java.time.LocalDate; @@ -12,7 +14,8 @@ public class LostRegisterRequestDto { private String title; private String content; - private LocalDate foundDate; + private String foundDate; + private MultipartFile image; private Double x; private Double y; private String dong; diff --git a/src/main/java/com/server/mappin/dto/LostRegisterResponseDto.java b/src/main/java/com/server/mappin/dto/LostRegisterResponseDto.java index 75a1474..6d8dab7 100644 --- a/src/main/java/com/server/mappin/dto/LostRegisterResponseDto.java +++ b/src/main/java/com/server/mappin/dto/LostRegisterResponseDto.java @@ -1,11 +1,27 @@ package com.server.mappin.dto; +import com.server.mappin.domain.Lost; import lombok.Builder; import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDate; +import java.time.LocalDateTime; @Data @Builder public class LostRegisterResponseDto { + private int statusCode; private String isSuccess; + private Long memberId; private Long lostId; + private String title; + private String content; + private LocalDate foundDate; + private LocalDateTime createdAt; + private String image; + private Double x; + private Double y; + private String dong; + private String category; } diff --git a/src/main/java/com/server/mappin/dto/PostCreateRequestDto.java b/src/main/java/com/server/mappin/dto/PostCreateRequestDto.java index aaba98e..075924e 100644 --- a/src/main/java/com/server/mappin/dto/PostCreateRequestDto.java +++ b/src/main/java/com/server/mappin/dto/PostCreateRequestDto.java @@ -13,7 +13,7 @@ public class PostCreateRequestDto { private LocalDate date; private String dong; private String category; - private int x; - private int y; + private Double x; + private Double y; } diff --git a/src/main/java/com/server/mappin/dto/PostCreateResponseDto.java b/src/main/java/com/server/mappin/dto/PostCreateResponseDto.java index b2a40b0..f18e4c0 100644 --- a/src/main/java/com/server/mappin/dto/PostCreateResponseDto.java +++ b/src/main/java/com/server/mappin/dto/PostCreateResponseDto.java @@ -6,6 +6,7 @@ @Data @Builder public class PostCreateResponseDto { + private int statusCode; private String isSuccess; private Long postId; diff --git a/src/main/java/com/server/mappin/dto/UserLoginResponseDto.java b/src/main/java/com/server/mappin/dto/UserLoginResponseDto.java new file mode 100644 index 0000000..3c32213 --- /dev/null +++ b/src/main/java/com/server/mappin/dto/UserLoginResponseDto.java @@ -0,0 +1,51 @@ +package com.server.mappin.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.Getter; + +@Data +@Builder +public class UserLoginResponseDto implements LoginResponseDto { + private int statusCode; + private String isSuccess; + private Long id; + private String role; + private String jwt; + private String token_type; + private long expires_in; + + @Override + public int getStatusCode(){ + return statusCode; + } + @Override + public String getIsSuccess() { + return isSuccess; + } + @Override + public Long getId() { + return id; + } + @Override + public String getRole() { + return role; + } + + @Override + public String getJwt() { + return jwt; + } + + @Override + public String getToken_Type() { + return token_type; + } + + @Override + public long getExpires_In() { + return expires_in; + } + + +} diff --git a/src/main/java/com/server/mappin/repository/LostRepository.java b/src/main/java/com/server/mappin/repository/LostRepository.java index aa43c12..af9f8f2 100644 --- a/src/main/java/com/server/mappin/repository/LostRepository.java +++ b/src/main/java/com/server/mappin/repository/LostRepository.java @@ -15,7 +15,10 @@ public interface LostRepository extends JpaRepository { @Query(value = "select l from Lost l left join fetch l.category c where c.name = :category") List findByCategory(@Param("category") String category); - + @Query(value = "select l from Lost l left join fetch l.location c where c.dong = :dong") + List findLocationByDong(@Param("dong") String dong); @Override S save(S entity); } + + diff --git a/src/main/java/com/server/mappin/repository/ShopRepository.java b/src/main/java/com/server/mappin/repository/ShopRepository.java index 3a964cb..98be2c0 100644 --- a/src/main/java/com/server/mappin/repository/ShopRepository.java +++ b/src/main/java/com/server/mappin/repository/ShopRepository.java @@ -1,10 +1,16 @@ package com.server.mappin.repository; import com.server.mappin.domain.Lost; +import com.server.mappin.domain.Member; import com.server.mappin.domain.Shop; +import org.springframework.data.domain.Example; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + @Repository public interface ShopRepository extends JpaRepository { + Optional findByMember(Member member); } diff --git a/src/main/java/com/server/mappin/service/LostService.java b/src/main/java/com/server/mappin/service/LostService.java index 1d2635d..ceeb0df 100644 --- a/src/main/java/com/server/mappin/service/LostService.java +++ b/src/main/java/com/server/mappin/service/LostService.java @@ -4,9 +4,7 @@ import com.server.mappin.domain.Location; import com.server.mappin.domain.Lost; import com.server.mappin.domain.Member; -import com.server.mappin.dto.FindByCategoryResponseDto; -import com.server.mappin.dto.LostRegisterRequestDto; -import com.server.mappin.dto.LostRegisterResponseDto; +import com.server.mappin.dto.*; import com.server.mappin.repository.CategoryRepository; import com.server.mappin.repository.LocationRepository; import com.server.mappin.repository.LostRepository; @@ -16,8 +14,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -30,22 +30,49 @@ public class LostService { private final MemberRepository memberRepository; private final CategoryRepository categoryRepository; private final LocationRepository locationRepository; - public List findByCategory(String categoryName){ + private final MapService mapService; + private final S3Service s3Service; + public FindByCategoryListResponseDto findByCategory(String categoryName){ List losts = lostRepository.findByCategory(categoryName); - List result = losts.stream() + List list = losts.stream() .map(lost -> FindByCategoryResponseDto.builder() .id(lost.getId()) .title(lost.getTitle()) .build()) .collect(Collectors.toList()); + return FindByCategoryListResponseDto.builder() + .statusCode(200) + .isSuccess("true") + .losts(list) + .build(); + } + public List findByDong(String dongName){ + List dongs = lostRepository.findLocationByDong(dongName); + List result = dongs.stream() + .map(dong -> FindByDongResponseDto.builder() + .id(dong.getId()) + .title(dong.getTitle()) + .dong(dong.getLocation().getDong()) + .imageUrl(dong.getImageUrl()) + .createdAt(dong.getCreatedAt()) + .build()) + .collect(Collectors.toList()); return result; } @Transactional - public LostRegisterResponseDto registerLost(LostRegisterRequestDto lostRegisterRequestDto, String email){ + public LostRegisterResponseDto registerLost(LostRegisterRequestDto lostRegisterRequestDto, String email) throws IOException { + //String으로 받아온 yyyy-MM-dd를 LocalDate 형식으로 변환 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDate localDate = LocalDate.parse(lostRegisterRequestDto.getFoundDate(),formatter); + //회원, 카테고리 찾기 Optional memberRepositoryByEmail = memberRepository.findByEmail(email); Optional categoryByName = categoryRepository.findCategoryByName(lostRegisterRequestDto.getCategory()); - Optional locationByDong = locationRepository.findLocationByDong(lostRegisterRequestDto.getDong()); + //x,y좌표로 동네 찾고 동네가 DB에 존재하는지 확인 + String dong = mapService.getDong(lostRegisterRequestDto.getX(), lostRegisterRequestDto.getY()); + Optional locationByDong = locationRepository.findLocationByDong(dong); + //이미지 S3에 업로드 + String imageUrl = s3Service.upload(lostRegisterRequestDto.getImage(), "images"); if(memberRepositoryByEmail.isPresent() && categoryByName.isPresent() && locationByDong.isPresent()){ Member member = memberRepositoryByEmail.get(); Location location = locationByDong.get(); @@ -55,7 +82,8 @@ public LostRegisterResponseDto registerLost(LostRegisterRequestDto lostRegisterR .content(lostRegisterRequestDto.getContent()) .x(lostRegisterRequestDto.getX()) .y(lostRegisterRequestDto.getY()) - .foundDate(lostRegisterRequestDto.getFoundDate()) + .foundDate(localDate) + .imageUrl(imageUrl) .createdAt(LocalDateTime.now()) .category(category) .location(location) @@ -63,11 +91,24 @@ public LostRegisterResponseDto registerLost(LostRegisterRequestDto lostRegisterR .build(); Lost save = lostRepository.save(lost); return LostRegisterResponseDto.builder() + .statusCode(200) .isSuccess("true") - .lostId(save.getId()) + .title(lost.getTitle()) + .content(lost.getContent()) + .x(lost.getX()) + .y(lost.getY()) + .foundDate(lost.getFoundDate()) + .createdAt(lost.getCreatedAt()) + .image(lost.getImageUrl()) + .category(lost.getCategory().getName()) + .memberId(lost.getMember().getId()) + .dong(location.getDong()) + .lostId(lost.getId()) .build(); + } return LostRegisterResponseDto.builder() + .statusCode(400) .isSuccess("false") .build(); diff --git a/src/main/java/com/server/mappin/service/MapService.java b/src/main/java/com/server/mappin/service/MapService.java new file mode 100644 index 0000000..43257ef --- /dev/null +++ b/src/main/java/com/server/mappin/service/MapService.java @@ -0,0 +1,83 @@ +package com.server.mappin.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.data.geo.Point; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +public class MapService { + + @Value("${spring.kakao.api.key}") + private String apiKey; + + public Point GetLocalInfo(String address){ + String apiUrl = "https://dapi.kakao.com/v2/local/search/address.json"; + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization","KakaoAK "+apiKey); + HttpEntity httpEntity = new HttpEntity<>(headers); + URI builder = UriComponentsBuilder + .fromUriString(apiUrl) + .queryParam("query",address) + .build() + .encode(StandardCharsets.UTF_8) + .toUri(); + ResponseEntity> response = new RestTemplate().exchange(builder, HttpMethod.GET, httpEntity, new ParameterizedTypeReference>() {}); + Map responseBody = response.getBody(); + List> documents = (List>)responseBody.get("documents"); + if(!documents.isEmpty()){ + Map firstDocument = documents.get(0); + Object xObject = firstDocument.get("x"); + Object yObject = firstDocument.get("y"); + if(xObject instanceof Double && yObject instanceof Double){ + Double xCoordinate = (Double) xObject; + Double yCoordinate = (Double) yObject; + Point point = new Point(xCoordinate,yCoordinate); + return new Point(xCoordinate,yCoordinate); + }else if(xObject instanceof String && yObject instanceof String){ + try{ + Double xCoordinate = Double.parseDouble((String) xObject); + Double yCoordinate = Double.parseDouble((String) yObject); + return new Point(xCoordinate,yCoordinate); + }catch (NumberFormatException e){ + log.error(e.getMessage()); + } + } + } + return null; + } + + public String getDong(Double xCoordinate, Double yCoordinate){ + String apiUrl = "https://dapi.kakao.com/v2/local/geo/coord2regioncode.json"; + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization","KakaoAK "+apiKey); + HttpEntity httpEntity = new HttpEntity<>(headers); + System.out.println("xCoordinate = " + xCoordinate); + URI builder = UriComponentsBuilder + .fromUriString(apiUrl) + .queryParam("x",xCoordinate) + .queryParam("y",yCoordinate) + .build() + .encode(StandardCharsets.UTF_8) + .toUri(); + ResponseEntity> response = new RestTemplate().exchange(builder, HttpMethod.GET, httpEntity, new ParameterizedTypeReference>() {}); + Map responseBody = response.getBody(); + List> documents = (List>)responseBody.get("documents"); + if(!documents.isEmpty()){ + Map firstDocument = documents.get(0); + Object dong = firstDocument.get("region_3depth_name"); + return (String)dong; + } + return null; + } +} diff --git a/src/main/java/com/server/mappin/service/MemberService.java b/src/main/java/com/server/mappin/service/MemberService.java index ef64af8..d9bf98d 100644 --- a/src/main/java/com/server/mappin/service/MemberService.java +++ b/src/main/java/com/server/mappin/service/MemberService.java @@ -2,18 +2,24 @@ import com.server.mappin.auth.token.TokenProvider; import com.server.mappin.domain.Member; +import com.server.mappin.domain.Shop; import com.server.mappin.domain.enums.ProviderType; +import com.server.mappin.domain.enums.Role; +import com.server.mappin.dto.AdminLoginResponseDto; import com.server.mappin.dto.LoginResponseDto; +import com.server.mappin.dto.UserLoginResponseDto; import com.server.mappin.dto.MemberLoginDto; import com.server.mappin.repository.MemberRepository; -import io.swagger.v3.oas.annotations.Parameters; +import com.server.mappin.repository.ShopRepository; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.geo.Point; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; -import java.time.LocalDateTime; +import java.util.Map; import java.util.Optional; @Service @@ -22,16 +28,14 @@ public class MemberService { private final MemberRepository memberRepository; private final TokenProvider tokenProvider; + private final ShopRepository shopRepository; + private final MapService mapService; @Value("${spring.jwt.access-token-validity-in-seconds}") long accessTokenValidityInMilliseconds; - @Transactional - public Optional findByEmail(String email) { - return memberRepository.findByEmail(email); - } @Transactional - public LoginResponseDto create(MemberLoginDto memberCreateDto) { + public LoginResponseDto login(MemberLoginDto memberCreateDto) { Member save; - + String jwt; Optional existingMember = memberRepository.findByEmail(memberCreateDto.getEmail()); if (existingMember.isPresent()) { @@ -43,18 +47,35 @@ public LoginResponseDto create(MemberLoginDto memberCreateDto) { member.setName(memberCreateDto.getName()); member.setCreatedAt(LocalDate.now()); member.setProviderType(ProviderType.KAKAO); - save = memberRepository.save(member); } - - String jwt = tokenProvider.generateToken(save.getEmail(), save.getRole().toString()); - - return LoginResponseDto.builder() + jwt = tokenProvider.generateToken(save.getEmail(), save.getRole().toString()); + if(save.getRole() == Role.OWNER){ + Optional byMember = shopRepository.findByMember(save); + if(byMember.isPresent()){ + Shop shop = byMember.get(); + Point point = mapService.GetLocalInfo(shop.getAddress()); + return AdminLoginResponseDto.builder() + .statusCode(201) + .isSuccess("true") + .id(save.getId()) + .jwt(jwt) + .token_type("Bearer") + .expires_in(accessTokenValidityInMilliseconds) + .role(save.getRole().name()) + .geo(point) + .build(); + } + } + return UserLoginResponseDto.builder() + .statusCode(200) .isSuccess("true") .id(save.getId()) + .role(save.getRole().name()) .jwt(jwt) .token_type("Bearer") .expires_in(accessTokenValidityInMilliseconds) .build(); + } } diff --git a/src/main/java/com/server/mappin/service/PostService.java b/src/main/java/com/server/mappin/service/PostService.java index 74b7528..83e3d9a 100644 --- a/src/main/java/com/server/mappin/service/PostService.java +++ b/src/main/java/com/server/mappin/service/PostService.java @@ -56,13 +56,16 @@ public PostCreateResponseDto create(PostCreateRequestDto postCreateRequestDto, S Post save = postRepository.save(post); log.info(save.getTitle()); return PostCreateResponseDto.builder() + .statusCode(200) .isSuccess("true") .postId(save.getId()) .build(); } return PostCreateResponseDto.builder() + .statusCode(400) .isSuccess("false") + .postId(null) .build(); } } diff --git a/src/main/java/com/server/mappin/service/S3Service.java b/src/main/java/com/server/mappin/service/S3Service.java new file mode 100644 index 0000000..fd3ff01 --- /dev/null +++ b/src/main/java/com/server/mappin/service/S3Service.java @@ -0,0 +1,62 @@ +package com.server.mappin.service; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.PutObjectRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Optional; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class S3Service { + + private final AmazonS3Client amazonS3Client; + + @Value("${cloud.aws.s3.bucket}") + public String bucket; // S3 버킷 + + // S3 파일 업로드 + public String upload(MultipartFile multipartFile, String dirName) throws IOException { + // MultipartFile -> File + File convertFile = convert(multipartFile) + .orElseThrow(() -> new IllegalArgumentException("file convert error")); // 파일을 변환할 수 없으면 에러 + + // S3에 저장할 파일명 + String fileName = dirName + "/" + UUID.randomUUID() + "_" + convertFile.getName(); + + // S3에 파일 업로드 + amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, convertFile).withCannedAcl(CannedAccessControlList.PublicRead)); + String uploadImageUrl = amazonS3Client.getUrl(bucket, fileName).toString(); + + // 로컬 파일 삭제 + convertFile.delete(); + + return uploadImageUrl; + } + + // S3 파일 삭제 + public void delete(String path) { + amazonS3Client.deleteObject(bucket, path); + } + + // 파일 convert 후 로컬에 업로드 + private Optional convert(MultipartFile file) throws IOException { + File convertFile = new File(System.getProperty("user.dir") + "/" + file.getOriginalFilename()); + if (convertFile.createNewFile()) { // 바로 위에서 지정한 경로에 File이 생성됨 (경로가 잘못되었다면 생성 불가능) + try (FileOutputStream fos = new FileOutputStream(convertFile)) { // FileOutputStream 데이터를 파일에 바이트 스트림으로 저장하기 위함 + fos.write(file.getBytes()); + } + return Optional.of(convertFile); + } + return Optional.empty(); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4a59e95..8b0db78 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -19,10 +19,13 @@ spring: url: ${DB_URL} username: ${DB_USERNAME} password: ${DB_PASS} - jpa : #jpa ?? + http: + encoding: + charset: UTF-8 + hibernate: - ddl-auto : create + ddl-auto : update properties: hibernate : #show_sql : true #??? ??? ?? @@ -53,10 +56,26 @@ spring: user-info-uri: https://kapi.kakao.com/v2/user/me user-name-attribute: id + kakao: + api: + key: ${KAKAO_REST_API_KEY} + # mvc: # pathmatch: # matching-strategy: ant_path_matcher +cloud: + aws: + credentials: + accessKey: ${S3Accesskey} # IAM 사용자 엑세스 키 + secretKey: ${S3SecretKey} # IAM 사용자 비밀 엑세스 키 + s3: + bucket: mappin-s3 # 버킷명 + region: + static: ap-northeast-2 # 리전 + stack: + auto: false # Spring Cloud는 환경 또는 스택을 기반으로 이를 자동으로 감지 + logging.level: org.hibernate.SQL: debug #??? ???? ??? ??? ??. org.hibernate.type : trace #???? ?????? ? ? ?