Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import danji.danjiapi.domain.auth.service.AuthService;
import danji.danjiapi.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -19,7 +20,8 @@ public class AuthController {
private final AuthService authService;

@PostMapping("/login")
@Operation(summary = "로그인", description = "이메일 아이디와 비밀번호로 로그인을 진행합니다. 로그인의 결과를 통해 회원의 역할을 구분합니다.")
@Operation(summary = "로그인", description = "이메일 아이디와 비밀번호로 로그인을 진행합니다. 로그인의 결과를 통해 회원의 역할을 구분합니다.",
security = @SecurityRequirement(name = ""))
public ApiResponse<AuthLoginResponse> login(@Valid @RequestBody AuthLoginRequest request) {
return ApiResponse.success(authService.login(request));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package danji.danjiapi.domain.market.controller;

import danji.danjiapi.domain.market.dto.request.MarketSearchCondition;
import danji.danjiapi.domain.market.dto.response.MarketSummary;
import danji.danjiapi.domain.market.service.MarketService;
import danji.danjiapi.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/markets")
@RequiredArgsConstructor
public class MarketController {
private final MarketService marketService;

@GetMapping("")
@Operation(summary = "가게 둘러보기", description = "회원이 모든 가게들의 목록을 조회하고, 키워드로 원하는 가게를 검색합니다.")
public ApiResponse<List<MarketSummary>> getMarkets(@ModelAttribute MarketSearchCondition searchCondition) {
return ApiResponse.success(marketService.searchMarkets(searchCondition));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package danji.danjiapi.domain.market.dto.request;

public record MarketSearchCondition(
String keyword
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package danji.danjiapi.domain.market.dto.response;

import danji.danjiapi.domain.market.entity.Market;

public record MarketSummary(
Long id,
String name,
String address,
String imageUrl
) {
public static MarketSummary from(Market market) {
return new MarketSummary(
market.getId(),
market.getName(),
market.getAddress(),
market.getImageUrl()
);
}
}
70 changes: 70 additions & 0 deletions src/main/java/danji/danjiapi/domain/market/entity/Market.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package danji.danjiapi.domain.market.entity;

import danji.danjiapi.domain.product.entity.Product;
import danji.danjiapi.domain.user.entity.User;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Table(name = "markets")
public class Market {

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

@Column(length = 30, nullable = false)
private String name;

@Column(length = 30, nullable = false)
private String address;

@Column(name = "image_url", nullable = false)
private String imageUrl;

@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;

@OneToOne
@JoinColumn(name = "user_id", nullable = false, unique = true)
private User user;

@OneToMany(mappedBy = "market", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Product> products = new ArrayList<>();

public static Market create(String name, String address, String imageUrl, User user) {
return Market.builder()
.name(name)
.address(address)
.imageUrl(imageUrl)
.user(user)
.build();
}

public void addProduct(Product product) {
products.add(product);
product.setMarket(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package danji.danjiapi.domain.market.repository;

import danji.danjiapi.domain.market.entity.Market;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface MarketRepository extends JpaRepository<Market, Long> {

@Query("""
SELECT DISTINCT m FROM Market m
LEFT JOIN m.products p
WHERE
(m.name LIKE %:keyword%
OR m.address LIKE %:keyword%
OR p.name LIKE %:keyword%)
""")
List<Market> findByNameOrAddressOrProductsContaining(@Param("keyword") String keyword);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package danji.danjiapi.domain.market.service;

import danji.danjiapi.domain.market.dto.request.MarketSearchCondition;
import danji.danjiapi.domain.market.dto.response.MarketSummary;
import danji.danjiapi.domain.market.entity.Market;
import danji.danjiapi.domain.market.repository.MarketRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class MarketService {
private final MarketRepository marketRepository;

public List<MarketSummary> searchMarkets(MarketSearchCondition searchCondition) {
List<Market> markets;

if (searchCondition == null || searchCondition.keyword() == null || searchCondition.keyword().trim().isEmpty()) {
markets = marketRepository.findAll();
} else {
markets = marketRepository.findByNameOrAddressOrProductsContaining(searchCondition.keyword().trim());
}

return markets.stream()
.map(MarketSummary::from)
.toList();
}
}
56 changes: 56 additions & 0 deletions src/main/java/danji/danjiapi/domain/product/entity/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package danji.danjiapi.domain.product.entity;

import danji.danjiapi.domain.market.entity.Market;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.CreatedDate;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Table(name = "products")
public class Product {

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

@Column(length = 20, nullable = false)
private String name;

@Column(nullable = false)
private BigDecimal price;

@Column
private Integer minQuantity;

@Column
private Integer maxQuantity;

@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;

@Setter
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "market_id", nullable = false)
private Market market;

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package danji.danjiapi.domain.user.controller;

import danji.danjiapi.domain.user.dto.request.UserCreateRequest;
import danji.danjiapi.domain.user.dto.response.UserCreateResponse;
import danji.danjiapi.domain.user.dto.request.UserCreateCustomerRequest;
import danji.danjiapi.domain.user.dto.request.UserCreateMerchantRequest;
import danji.danjiapi.domain.user.dto.response.UserCreateMerchantResponse;
import danji.danjiapi.domain.user.dto.response.UserCreateCustomerResponse;
import danji.danjiapi.domain.user.service.UserService;
import danji.danjiapi.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -18,9 +21,17 @@
public class UserController {
private final UserService userService;

@PostMapping("/signup")
@Operation(summary = "회원 가입", description = "이메일 아이디로 회원 가입을 진행합니다.")
public ApiResponse<UserCreateResponse> signup(@Valid @RequestBody UserCreateRequest request) {
return ApiResponse.success(userService.signup(request));
@PostMapping("/signup/customer")
@Operation(summary = "일반 회원 가입", description = "일반 회원의 회원 가입을 진행합니다.",
security = @SecurityRequirement(name = ""))
public ApiResponse<UserCreateCustomerResponse> signupCustomer(@Valid @RequestBody UserCreateCustomerRequest request) {
return ApiResponse.success(userService.signupCustomer(request));
}

@PostMapping("/signup/merchant")
@Operation(summary = "사장님 회원 가입", description = "사장님의 회원 가입을 진행합니다. 회원 가입과 가게 생성이 하나의 프로세스로 진행됩니다.",
security = @SecurityRequirement(name = ""))
public ApiResponse<UserCreateMerchantResponse> signupMerchant(@Valid @RequestBody UserCreateMerchantRequest request) {
return ApiResponse.success(userService.signupMerchant(request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public record UserCreateRequest(
public record UserCreateCustomerRequest(
@NotBlank(message = "이메일은 필수입니다.")
@Email(message = "올바른 이메일 형식이 아닙니다.")
String email,
@NotBlank(message = "비밀번호는 필수입니다")
String password,
@NotBlank(message = "이름은 필수입니다")
String name,
@NotBlank(message = "역할 정보가 누락되었습니다")
String role
String name
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package danji.danjiapi.domain.user.dto.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public record UserCreateMerchantRequest(
@NotBlank(message = "이메일은 필수입니다.")
@Email(message = "올바른 이메일 형식이 아닙니다.")
String email,
@NotBlank(message = "비밀번호는 필수입니다")
String password,
@NotBlank(message = "이름은 필수입니다")
String name,

@NotBlank(message = "상호명은 필수입니다.")
String marketName,
@NotBlank(message = "주소는 필수입니다.")
String marketAddress,
String marketImageUrl
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
import lombok.Builder;

@Builder
public record UserCreateResponse(
public record UserCreateCustomerResponse(
Long id,
String email,
String name,
String role
) {
public static UserCreateResponse from(Long id, String email, String name, String role) {
return UserCreateResponse.builder()
public static UserCreateCustomerResponse from(Long id, String name, String role) {
return UserCreateCustomerResponse.builder()
.id(id)
.email(email)
.name(name)
.role(role)
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package danji.danjiapi.domain.user.dto.response;

import lombok.Builder;

@Builder
public record UserCreateMerchantResponse(
Long id,
String name,
String role,
Long marketId
) {
public static UserCreateMerchantResponse from(Long id, String name, String role, Long marketId) {
return UserCreateMerchantResponse.builder()
.id(id)
.name(name)
.role(role)
.marketId(marketId)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByEmail(String email);
Optional<User> findByEmail(String email);
}
Loading