Skip to content
48 changes: 34 additions & 14 deletions src/main/java/com/arom/with_travel/domain/community/Community.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import com.arom.with_travel.global.entity.BaseEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

Expand All @@ -18,35 +16,57 @@
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@AllArgsConstructor
@SQLDelete(sql = "UPDATE community SET is_deleted = true, deleted_at = now() where id = ?")
@SQLRestriction("is_deleted is FALSE")
public class Community extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull

@NotNull @Column(length = 120)
private String title;

@NotNull
@NotNull @Lob
private String content;

@NotNull
private String continent;

@NotNull
private String country;

@NotNull
private String city;
@NotNull private String continent;
@NotNull private String country;
@NotNull private String city;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
@JoinColumn(name = "member_id", nullable = false)
private Member member;

@OneToMany(mappedBy = "community")
private List<CommunityReply> communityReplies = new ArrayList<>();

@OneToMany(mappedBy = "community")
private List<Image> images = new ArrayList<>();

@Column(nullable = false)
private long viewCount = 0L;

public static Community create(Member writer, String title, String content,
String continent, String country, String city) {
Community c = Community.builder()
.member(writer)
.title(title)
.content(content)
.continent(continent)
.country(country)
.city(city)
.build();
return c;
}
Comment on lines +52 to +63
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

연관관계 양방향 매핑도 추가해주세요


public void update(String title, String content, String continent, String country, String city) {
if (title != null) this.title = title;
if (content != null) this.content = content;
if (continent != null) this.continent = continent;
if (country != null) this.country = country;
if (city != null) this.city = city;
}
Comment on lines +65 to +71
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null 여부를 확인하지 말고 그냥 값을 초기화하면 될것 같습니다. 게시글 수정 시 수정된 부분만 받는것이 아닌 전체 수정으로 받을 것 같기 때문입니다.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.arom.with_travel.domain.community;

import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.StringUtils;

public class CommunitySpecs {
private CommunitySpecs() {}

Comment on lines +6 to +8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 목적으로 만든 클래스인가요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JPA Specification 모아 둔 유틸 클래스인데 대륙/국가/도시/검색 키워드 조건을 동적으로 조합해서 커뮤니티 게시글 검색할 때 사용하려고 만든 거예요

public static Specification<Community> continentEq(String continent) {
return (root, q, cb) -> StringUtils.hasText(continent) ? cb.equal(root.get("continent"), continent) : null;
}

public static Specification<Community> countryEq(String country) {
return (root, q, cb) -> StringUtils.hasText(country) ? cb.equal(root.get("country"), country) : null;
}

public static Specification<Community> cityEq(String city) {
return (root, q, cb) -> StringUtils.hasText(city) ? cb.equal(root.get("city"), city) : null;
}

public static Specification<Community> keywordLike(String qword) {
return (root, q, cb) -> {
if (!StringUtils.hasText(qword)) return null;
String like = "%" + qword.trim() + "%";
return cb.or(
cb.like(root.get("title"), like),
cb.like(root.get("content"), like)
);
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.arom.with_travel.domain.community.controller;

import com.arom.with_travel.domain.community.dto.CommunityCreateRequest;
import com.arom.with_travel.domain.community.dto.CommunityDetailResponse;
import com.arom.with_travel.domain.community.dto.CommunityListItemResponse;
import com.arom.with_travel.domain.community.dto.CommunityUpdateRequest;
import com.arom.with_travel.domain.community.service.CommunityService;
import com.arom.with_travel.global.security.domain.PrincipalDetails;
import com.arom.with_travel.global.utils.SecurityUtils;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/communities")
public class CommunityController {

private final CommunityService communityService;

@PostMapping
public Long create(@AuthenticationPrincipal PrincipalDetails principal,
@RequestBody @Valid CommunityCreateRequest req) {
String me = principal.getAuthenticatedMember().getEmail();
return communityService.create(me, req);
}

@GetMapping("/{id}")
public CommunityDetailResponse detail(@PathVariable Long id) {
return communityService.readAndIncreaseView(id);
}

@GetMapping
public Page<CommunityListItemResponse> list(
@RequestParam(required = false) String continent,
@RequestParam(required = false) String country,
@RequestParam(required = false) String city,
@RequestParam(required = false, name = "q") String keyword,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {

Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
return communityService.search(continent, country, city, keyword, pageable);
}

@PatchMapping("/{id}")
public void update(@PathVariable Long id, @RequestBody @Valid CommunityUpdateRequest req) {
Long me = SecurityUtils.currentMemberIdOrThrow();
communityService.update(me, id, req);
}

@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
Long me = SecurityUtils.currentMemberIdOrThrow();
communityService.delete(me, id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.arom.with_travel.domain.community.dto;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CommunityCreateRequest {
@NotEmpty @Size(max = 120) String title;
@NotEmpty String content;
@NotEmpty String continent;
@NotEmpty String country;
@NotEmpty String city;
private List<ImageCreate> images;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ImageCreate {
private String imageName;
private String imageUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.arom.with_travel.domain.community.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CommunityDetailResponse {
Long id;
String title;
String content;
String continent;
String country;
String city;
Long writerId;
String writerNickname;
long viewCount;
List<String> imageUrls;
String createdAt;
String updatedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.arom.with_travel.domain.community.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CommunityListItemResponse {
Long id;
String title;
String snippet;
String continent;
String country;
String city;
Long writerId;
String writerNickname;
long viewCount;
String createdAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.arom.with_travel.domain.community.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CommunityUpdateRequest {
private String title;
private String content;
private String continent;
private String country;
private String city;
private List<ImageUpdate> images;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ImageUpdate {
private String imageName;
private String imageUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.arom.with_travel.domain.community.repository;

import com.arom.with_travel.domain.community.Community;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.*;

public interface CommunityRepository extends JpaRepository<Community, Long>,
JpaSpecificationExecutor<Community> {

@EntityGraph(attributePaths = {"member", "images"})
@Query("select c from Community c where c.id = :id")
Community findDetailById(Long id);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("update Community c set c.viewCount = c.viewCount + 1 where c.id = :id")
int increaseViewCount(Long id);

default Page<Community> search(Specification<Community> spec, Pageable pageable) {
return this.findAll(spec, pageable);
}
}
Loading