mirror of
https://github.com/won-ktds/smarketing-backend.git
synced 2026-06-13 04:49:10 +00:00
modify: folder - java/python
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
package com.won.smarketing.store;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
/**
|
||||
* 매장 서비스 메인 애플리케이션 클래스
|
||||
* Spring Boot 애플리케이션의 진입점
|
||||
*/
|
||||
@SpringBootApplication(scanBasePackages = {"com.won.smarketing.store", "com.won.smarketing.common"})
|
||||
@EntityScan(basePackages = {"com.won.smarketing.store.entity"})
|
||||
@EnableJpaRepositories(basePackages = {"com.won.smarketing.store.repository"})
|
||||
public class StoreServiceApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(StoreServiceApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.won.smarketing.store.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
|
||||
/**
|
||||
* JPA 설정 클래스
|
||||
* JPA Auditing 기능 활성화
|
||||
*/
|
||||
@Configuration
|
||||
@EnableJpaAuditing
|
||||
public class JpaConfig {
|
||||
}리는 50자 이하여야 합니다")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "가격", example = "4500", required = true)
|
||||
@NotNull(message = "가격은 필수입니다")
|
||||
@Min(value = 0, message = "가격은 0원 이상이어야 합니다")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "메뉴 설명", example = "진한 맛의 아메리카노")
|
||||
@Size(max = 500, message = "메뉴 설명은 500자 이하여야 합니다")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "이미지 URL", example = "https://example.com/americano.jpg")
|
||||
@Size(max = 500, message = "이미지 URL은 500자 이하여야 합니다")
|
||||
private String image;
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package com.won.smarketing.store.controller;
|
||||
|
||||
import com.won.smarketing.common.dto.ApiResponse;
|
||||
import com.won.smarketing.store.dto.MenuCreateRequest;
|
||||
import com.won.smarketing.store.dto.MenuResponse;
|
||||
import com.won.smarketing.store.dto.MenuUpdateRequest;
|
||||
import com.won.smarketing.store.service.MenuService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 메뉴 관리를 위한 REST API 컨트롤러
|
||||
* 메뉴 등록, 조회, 수정, 삭제 기능 제공
|
||||
*/
|
||||
@Tag(name = "메뉴 관리", description = "메뉴 정보 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/menu")
|
||||
@RequiredArgsConstructor
|
||||
public class MenuController {
|
||||
|
||||
private final MenuService menuService;
|
||||
|
||||
/**
|
||||
* 메뉴 정보 등록
|
||||
*
|
||||
* @param request 메뉴 등록 요청 정보
|
||||
* @return 등록된 메뉴 정보
|
||||
*/
|
||||
@Operation(summary = "메뉴 등록", description = "새로운 메뉴를 등록합니다.")
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<ApiResponse<MenuResponse>> register(@Valid @RequestBody MenuCreateRequest request) {
|
||||
MenuResponse response = menuService.register(request);
|
||||
return ResponseEntity.ok(ApiResponse.success(response, "메뉴가 성공적으로 등록되었습니다."));
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 목록 조회
|
||||
*
|
||||
* @param category 메뉴 카테고리 (선택사항)
|
||||
* @return 메뉴 목록
|
||||
*/
|
||||
@Operation(summary = "메뉴 목록 조회", description = "메뉴 목록을 조회합니다. 카테고리별 필터링 가능합니다.")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<List<MenuResponse>>> getMenus(
|
||||
@Parameter(description = "메뉴 카테고리")
|
||||
@RequestParam(required = false) String category) {
|
||||
List<MenuResponse> response = menuService.getMenus(category);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 정보 수정
|
||||
*
|
||||
* @param menuId 수정할 메뉴 ID
|
||||
* @param request 메뉴 수정 요청 정보
|
||||
* @return 수정된 메뉴 정보
|
||||
*/
|
||||
@Operation(summary = "메뉴 수정", description = "메뉴 정보를 수정합니다.")
|
||||
@PutMapping("/{menuId}")
|
||||
public ResponseEntity<ApiResponse<MenuResponse>> updateMenu(
|
||||
@Parameter(description = "메뉴 ID", required = true)
|
||||
@PathVariable Long menuId,
|
||||
@Valid @RequestBody MenuUpdateRequest request) {
|
||||
MenuResponse response = menuService.updateMenu(menuId, request);
|
||||
return ResponseEntity.ok(ApiResponse.success(response, "메뉴가 성공적으로 수정되었습니다."));
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 삭제
|
||||
*
|
||||
* @param menuId 삭제할 메뉴 ID
|
||||
* @return 삭제 성공 응답
|
||||
*/
|
||||
@Operation(summary = "메뉴 삭제", description = "메뉴를 삭제합니다.")
|
||||
@DeleteMapping("/{menuId}")
|
||||
public ResponseEntity<ApiResponse<Void>> deleteMenu(
|
||||
@Parameter(description = "메뉴 ID", required = true)
|
||||
@PathVariable Long menuId) {
|
||||
menuService.deleteMenu(menuId);
|
||||
return ResponseEntity.ok(ApiResponse.success(null, "메뉴가 성공적으로 삭제되었습니다."));
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.won.smarketing.store.controller;
|
||||
|
||||
import com.won.smarketing.common.dto.ApiResponse;
|
||||
import com.won.smarketing.store.dto.SalesResponse;
|
||||
import com.won.smarketing.store.service.SalesService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 매출 정보를 위한 REST API 컨트롤러
|
||||
* 매출 조회 기능 제공
|
||||
*/
|
||||
@Tag(name = "매출 관리", description = "매출 정보 조회 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/sales")
|
||||
@RequiredArgsConstructor
|
||||
public class SalesController {
|
||||
|
||||
private final SalesService salesService;
|
||||
|
||||
/**
|
||||
* 매출 정보 조회
|
||||
*
|
||||
* @return 매출 정보 (오늘, 월간, 전일 대비)
|
||||
*/
|
||||
@Operation(summary = "매출 조회", description = "오늘 매출, 월간 매출, 전일 대비 매출 정보를 조회합니다.")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<SalesResponse>> getSales() {
|
||||
SalesResponse response = salesService.getSales();
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.won.smarketing.store.controller;
|
||||
|
||||
import com.won.smarketing.common.dto.ApiResponse;
|
||||
import com.won.smarketing.store.dto.StoreCreateRequest;
|
||||
import com.won.smarketing.store.dto.StoreResponse;
|
||||
import com.won.smarketing.store.dto.StoreUpdateRequest;
|
||||
import com.won.smarketing.store.service.StoreService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 매장 관리를 위한 REST API 컨트롤러
|
||||
* 매장 등록, 조회, 수정 기능 제공
|
||||
*/
|
||||
@Tag(name = "매장 관리", description = "매장 정보 관리 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/store")
|
||||
@RequiredArgsConstructor
|
||||
public class StoreController {
|
||||
|
||||
private final StoreService storeService;
|
||||
|
||||
/**
|
||||
* 매장 정보 등록
|
||||
*
|
||||
* @param request 매장 등록 요청 정보
|
||||
* @return 등록된 매장 정보
|
||||
*/
|
||||
@Operation(summary = "매장 등록", description = "새로운 매장 정보를 등록합니다.")
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<ApiResponse<StoreResponse>> register(@Valid @RequestBody StoreCreateRequest request) {
|
||||
StoreResponse response = storeService.register(request);
|
||||
return ResponseEntity.ok(ApiResponse.success(response, "매장이 성공적으로 등록되었습니다."));
|
||||
}
|
||||
|
||||
/**
|
||||
* 매장 정보 조회
|
||||
*
|
||||
* @param storeId 조회할 매장 ID
|
||||
* @return 매장 정보
|
||||
*/
|
||||
@Operation(summary = "매장 조회", description = "매장 ID로 매장 정보를 조회합니다.")
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<StoreResponse>> getStore(
|
||||
@Parameter(description = "매장 ID", required = true)
|
||||
@RequestParam String storeId) {
|
||||
StoreResponse response = storeService.getStore(storeId);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* 매장 정보 수정
|
||||
*
|
||||
* @param storeId 수정할 매장 ID
|
||||
* @param request 매장 수정 요청 정보
|
||||
* @return 수정된 매장 정보
|
||||
*/
|
||||
@Operation(summary = "매장 수정", description = "매장 정보를 수정합니다.")
|
||||
@PutMapping("/{storeId}")
|
||||
public ResponseEntity<ApiResponse<StoreResponse>> updateStore(
|
||||
@Parameter(description = "매장 ID", required = true)
|
||||
@PathVariable Long storeId,
|
||||
@Valid @RequestBody StoreUpdateRequest request) {
|
||||
StoreResponse response = storeService.updateStore(storeId, request);
|
||||
return ResponseEntity.ok(ApiResponse.success(response, "매장 정보가 성공적으로 수정되었습니다."));
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.won.smarketing.store.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 메뉴 등록 요청 DTO
|
||||
* 메뉴 등록 시 필요한 정보를 전달합니다.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "메뉴 등록 요청")
|
||||
public class MenuCreateRequest {
|
||||
|
||||
@Schema(description = "매장 ID", example = "1", required = true)
|
||||
@NotNull(message = "매장 ID는 필수입니다")
|
||||
private Long storeId;
|
||||
|
||||
@Schema(description = "메뉴명", example = "아메리카노", required = true)
|
||||
@NotBlank(message = "메뉴명은 필수입니다")
|
||||
@Size(max = 100, message = "메뉴명은 100자 이하여야 합니다")
|
||||
private String menuName;
|
||||
|
||||
@Schema(description = "카테고리", example = "커피")
|
||||
@Size(max = 50, message = "카테고리는 50자 이하여야 합니다")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "가격", example = "4500")
|
||||
@Min(value = 0, message = "가격은 0원 이상이어야 합니다")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "메뉴 설명", example = "진한 맛의 아메리카노")
|
||||
@Size(max = 500, message = "메뉴 설명은 500자 이하여야 합니다")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "이미지 URL", example = "https://example.com/americano.jpg")
|
||||
@Size(max = 500, message = "이미지 URL은 500자 이하여야 합니다")
|
||||
private String image;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.won.smarketing.store.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 메뉴 응답 DTO
|
||||
* 메뉴 정보를 클라이언트에게 전달합니다.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "메뉴 응답")
|
||||
public class MenuResponse {
|
||||
|
||||
@Schema(description = "메뉴 ID", example = "1")
|
||||
private Long menuId;
|
||||
|
||||
@Schema(description = "매장 ID", example = "1")
|
||||
private Long storeId;
|
||||
|
||||
@Schema(description = "메뉴명", example = "아메리카노")
|
||||
private String menuName;
|
||||
|
||||
@Schema(description = "카테고리", example = "커피")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "가격", example = "4500")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "메뉴 설명", example = "진한 맛의 아메리카노")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "이미지 URL", example = "https://example.com/americano.jpg")
|
||||
private String image;
|
||||
|
||||
@Schema(description = "등록일시", example = "2024-01-15T10:30:00")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "수정일시", example = "2024-01-15T10:30:00")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.won.smarketing.store.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 메뉴 수정 요청 DTO
|
||||
* 메뉴 정보 수정 시 필요한 정보를 담는 데이터 전송 객체
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "메뉴 수정 요청 정보")
|
||||
public class MenuUpdateRequest {
|
||||
|
||||
@Schema(description = "메뉴명", example = "아메리카노")
|
||||
@Size(max = 200, message = "메뉴명은 200자 이하여야 합니다.")
|
||||
private String menuName;
|
||||
|
||||
@Schema(description = "메뉴 카테고리", example = "커피")
|
||||
@Size(max = 100, message = "카테고리는 100자 이하여야 합니다.")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "가격", example = "4500")
|
||||
@Min(value = 0, message = "가격은 0 이상이어야 합니다.")
|
||||
private Integer price;
|
||||
|
||||
@Schema(description = "메뉴 설명", example = "진한 원두의 깊은 맛")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "메뉴 이미지 URL", example = "https://example.com/americano.jpg")
|
||||
private String image;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.won.smarketing.store.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 매출 응답 DTO
|
||||
* 매출 정보를 클라이언트에게 전달합니다.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "매출 응답")
|
||||
public class SalesResponse {
|
||||
|
||||
@Schema(description = "오늘 매출", example = "150000")
|
||||
private BigDecimal todaySales;
|
||||
|
||||
@Schema(description = "월간 매출", example = "4500000")
|
||||
private BigDecimal monthSales;
|
||||
|
||||
@Schema(description = "전일 대비 매출 변화", example = "25000")
|
||||
private BigDecimal previousDayComparison;
|
||||
|
||||
@Schema(description = "전일 대비 매출 변화율 (%)", example = "15.5")
|
||||
private BigDecimal previousDayChangeRate;
|
||||
|
||||
@Schema(description = "목표 매출 대비 달성율 (%)", example = "85.2")
|
||||
private BigDecimal goalAchievementRate;
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.won.smarketing.store.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 매장 등록 요청 DTO
|
||||
* 매장 등록 시 필요한 정보를 전달합니다.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "매장 등록 요청")
|
||||
public class StoreCreateRequest {
|
||||
|
||||
@Schema(description = "매장명", example = "맛있는 카페", required = true)
|
||||
@NotBlank(message = "매장명은 필수입니다")
|
||||
@Size(max = 100, message = "매장명은 100자 이하여야 합니다")
|
||||
private String storeName;
|
||||
|
||||
@Schema(description = "업종", example = "카페")
|
||||
@Size(max = 50, message = "업종은 50자 이하여야 합니다")
|
||||
private String businessType;
|
||||
|
||||
@Schema(description = "주소", example = "서울시 강남구 테헤란로 123", required = true)
|
||||
@NotBlank(message = "주소는 필수입니다")
|
||||
@Size(max = 200, message = "주소는 200자 이하여야 합니다")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "전화번호", example = "02-1234-5678")
|
||||
@Size(max = 20, message = "전화번호는 20자 이하여야 합니다")
|
||||
private String phoneNumber;
|
||||
|
||||
@Schema(description = "영업시간", example = "09:00 - 22:00")
|
||||
@Size(max = 100, message = "영업시간은 100자 이하여야 합니다")
|
||||
private String businessHours;
|
||||
|
||||
@Schema(description = "휴무일", example = "매주 일요일")
|
||||
@Size(max = 100, message = "휴무일은 100자 이하여야 합니다")
|
||||
private String closedDays;
|
||||
|
||||
@Schema(description = "좌석 수", example = "20")
|
||||
private Integer seatCount;
|
||||
|
||||
@Schema(description = "SNS 계정 정보", example = "인스타그램: @mystore")
|
||||
@Size(max = 500, message = "SNS 계정 정보는 500자 이하여야 합니다")
|
||||
private String snsAccounts;
|
||||
|
||||
@Schema(description = "매장 설명", example = "따뜻한 분위기의 동네 카페입니다.")
|
||||
@Size(max = 1000, message = "매장 설명은 1000자 이하여야 합니다")
|
||||
private String description;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.won.smarketing.store.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 매장 응답 DTO
|
||||
* 매장 정보를 클라이언트에게 전달합니다.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Schema(description = "매장 응답")
|
||||
public class StoreResponse {
|
||||
|
||||
@Schema(description = "매장 ID", example = "1")
|
||||
private Long storeId;
|
||||
|
||||
@Schema(description = "매장명", example = "맛있는 카페")
|
||||
private String storeName;
|
||||
|
||||
@Schema(description = "업종", example = "카페")
|
||||
private String businessType;
|
||||
|
||||
@Schema(description = "주소", example = "서울시 강남구 테헤란로 123")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "전화번호", example = "02-1234-5678")
|
||||
private String phoneNumber;
|
||||
|
||||
@Schema(description = "영업시간", example = "09:00 - 22:00")
|
||||
private String businessHours;
|
||||
|
||||
@Schema(description = "휴무일", example = "매주 일요일")
|
||||
private String closedDays;
|
||||
|
||||
@Schema(description = "좌석 수", example = "20")
|
||||
private Integer seatCount;
|
||||
|
||||
@Schema(description = "SNS 계정 정보", example = "인스타그램: @mystore")
|
||||
private String snsAccounts;
|
||||
|
||||
@Schema(description = "매장 설명", example = "따뜻한 분위기의 동네 카페입니다.")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "등록일시", example = "2024-01-15T10:30:00")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "수정일시", example = "2024-01-15T10:30:00")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.won.smarketing.store.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 매장 수정 요청 DTO
|
||||
* 매장 정보 수정 시 필요한 정보를 전달합니다.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "매장 수정 요청")
|
||||
public class StoreUpdateRequest {
|
||||
|
||||
@Schema(description = "매장명", example = "맛있는 카페")
|
||||
@Size(max = 100, message = "매장명은 100자 이하여야 합니다")
|
||||
private String storeName;
|
||||
|
||||
@Schema(description = "업종", example = "카페")
|
||||
@Size(max = 50, message = "업종은 50자 이하여야 합니다")
|
||||
private String businessType;
|
||||
|
||||
@Schema(description = "주소", example = "서울시 강남구 테헤란로 123")
|
||||
@Size(max = 200, message = "주소는 200자 이하여야 합니다")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "전화번호", example = "02-1234-5678")
|
||||
@Size(max = 20, message = "전화번호는 20자 이하여야 합니다")
|
||||
private String phoneNumber;
|
||||
|
||||
@Schema(description = "영업시간", example = "09:00 - 22:00")
|
||||
@Size(max = 100, message = "영업시간은 100자 이하여야 합니다")
|
||||
private String businessHours;
|
||||
|
||||
@Schema(description = "휴무일", example = "매주 일요일")
|
||||
@Size(max = 100, message = "휴무일은 100자 이하여야 합니다")
|
||||
private String closedDays;
|
||||
|
||||
@Schema(description = "좌석 수", example = "20")
|
||||
private Integer seatCount;
|
||||
|
||||
@Schema(description = "SNS 계정 정보", example = "인스타그램: @mystore")
|
||||
@Size(max = 500, message = "SNS 계정 정보는 500자 이하여야 합니다")
|
||||
private String snsAccounts;
|
||||
|
||||
@Schema(description = "매장 설명", example = "따뜻한 분위기의 동네 카페입니다.")
|
||||
@Size(max = 1000, message = "매장 설명은 1000자 이하여야 합니다")
|
||||
private String description;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.won.smarketing.store.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 메뉴 엔티티
|
||||
* 매장의 메뉴 정보를 관리
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "menus")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class Menu {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "menu_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "store_id", nullable = false)
|
||||
private Long storeId;
|
||||
|
||||
@Column(name = "menu_name", nullable = false, length = 100)
|
||||
private String menuName;
|
||||
|
||||
@Column(name = "category", length = 50)
|
||||
private String category;
|
||||
|
||||
@Column(name = "price", nullable = false)
|
||||
private Integer price;
|
||||
|
||||
@Column(name = "description", length = 500)
|
||||
private String description;
|
||||
|
||||
@Column(name = "image_url", length = 500)
|
||||
private String image;
|
||||
|
||||
@CreatedDate
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@LastModifiedDate
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 메뉴 정보 업데이트
|
||||
*
|
||||
* @param menuName 메뉴명
|
||||
* @param category 카테고리
|
||||
* @param price 가격
|
||||
* @param description 설명
|
||||
* @param image 이미지 URL
|
||||
*/
|
||||
public void updateMenu(String menuName, String category, Integer price,
|
||||
String description, String image) {
|
||||
if (menuName != null && !menuName.trim().isEmpty()) {
|
||||
this.menuName = menuName;
|
||||
}
|
||||
if (category != null && !category.trim().isEmpty()) {
|
||||
this.category = category;
|
||||
}
|
||||
if (price != null && price > 0) {
|
||||
this.price = price;
|
||||
}
|
||||
this.description = description;
|
||||
this.image = image;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.won.smarketing.store.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 매출 정보를 나타내는 엔티티
|
||||
* 일별 매출 데이터 저장
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "sales")
|
||||
@Getter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class Sales {
|
||||
|
||||
/**
|
||||
* 매출 고유 식별자
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 매장 ID
|
||||
*/
|
||||
@Column(name = "store_id", nullable = false)
|
||||
private Long storeId;
|
||||
|
||||
/**
|
||||
* 매출 날짜
|
||||
*/
|
||||
@Column(name = "sales_date", nullable = false)
|
||||
private LocalDate salesDate;
|
||||
|
||||
/**
|
||||
* 매출 금액
|
||||
*/
|
||||
@Column(name = "sales_amount", nullable = false, precision = 15, scale = 2)
|
||||
private BigDecimal salesAmount;
|
||||
|
||||
/**
|
||||
* 매출 등록 시각
|
||||
*/
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 엔티티 저장 전 실행되는 메서드
|
||||
* 생성 시각을 현재 시각으로 설정
|
||||
*/
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.won.smarketing.store.entity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* 매장 엔티티
|
||||
* 매장의 기본 정보와 운영 정보를 관리
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "stores")
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class Store {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "store_id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "member_id", nullable = false)
|
||||
private Long memberId;
|
||||
|
||||
@Column(name = "store_name", nullable = false, length = 100)
|
||||
private String storeName;
|
||||
|
||||
@Column(name = "business_type", length = 50)
|
||||
private String businessType;
|
||||
|
||||
@Column(name = "address", nullable = false, length = 200)
|
||||
private String address;
|
||||
|
||||
@Column(name = "phone_number", length = 20)
|
||||
private String phoneNumber;
|
||||
|
||||
@Column(name = "business_hours", length = 100)
|
||||
private String businessHours;
|
||||
|
||||
@Column(name = "closed_days", length = 100)
|
||||
private String closedDays;
|
||||
|
||||
@Column(name = "seat_count")
|
||||
private Integer seatCount;
|
||||
|
||||
@Column(name = "sns_accounts", length = 500)
|
||||
private String snsAccounts;
|
||||
|
||||
@Column(name = "description", length = 1000)
|
||||
private String description;
|
||||
|
||||
@CreatedDate
|
||||
@Column(name = "created_at", nullable = false, updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@LastModifiedDate
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 매장 정보 업데이트
|
||||
*
|
||||
* @param storeName 매장명
|
||||
* @param businessType 업종
|
||||
* @param address 주소
|
||||
* @param phoneNumber 전화번호
|
||||
* @param businessHours 영업시간
|
||||
* @param closedDays 휴무일
|
||||
* @param seatCount 좌석 수
|
||||
* @param snsAccounts SNS 계정 정보
|
||||
* @param description 설명
|
||||
*/
|
||||
public void updateStore(String storeName, String businessType, String address,
|
||||
String phoneNumber, String businessHours, String closedDays,
|
||||
Integer seatCount, String snsAccounts, String description) {
|
||||
if (storeName != null && !storeName.trim().isEmpty()) {
|
||||
this.storeName = storeName;
|
||||
}
|
||||
if (businessType != null && !businessType.trim().isEmpty()) {
|
||||
this.businessType = businessType;
|
||||
}
|
||||
if (address != null && !address.trim().isEmpty()) {
|
||||
this.address = address;
|
||||
}
|
||||
this.phoneNumber = phoneNumber;
|
||||
this.businessHours = businessHours;
|
||||
this.closedDays = closedDays;
|
||||
this.seatCount = seatCount;
|
||||
this.snsAccounts = snsAccounts;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.won.smarketing.store.repository;
|
||||
|
||||
import com.won.smarketing.store.entity.Menu;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 메뉴 정보 데이터 접근을 위한 Repository
|
||||
* JPA를 사용한 메뉴 CRUD 작업 처리
|
||||
*/
|
||||
@Repository
|
||||
public interface MenuRepository extends JpaRepository<Menu, Long> {
|
||||
|
||||
/**
|
||||
* 카테고리별 메뉴 조회 (메뉴명 오름차순)
|
||||
*
|
||||
* @param category 메뉴 카테고리
|
||||
* @return 메뉴 목록
|
||||
*/
|
||||
List<Menu> findByCategoryOrderByMenuNameAsc(String category);
|
||||
|
||||
/**
|
||||
* 전체 메뉴 조회 (메뉴명 오름차순)
|
||||
*
|
||||
* @return 메뉴 목록
|
||||
*/
|
||||
List<Menu> findAllByOrderByMenuNameAsc();
|
||||
|
||||
/**
|
||||
* 매장별 메뉴 조회
|
||||
*
|
||||
* @param storeId 매장 ID
|
||||
* @return 메뉴 목록
|
||||
*/
|
||||
List<Menu> findByStoreId(Long storeId);
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.won.smarketing.store.repository;
|
||||
|
||||
import com.won.smarketing.store.entity.Sales;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 매출 정보 데이터 접근을 위한 Repository
|
||||
* JPA를 사용한 매출 조회 작업 처리
|
||||
*/
|
||||
@Repository
|
||||
public interface SalesRepository extends JpaRepository<Sales, Long> {
|
||||
|
||||
/**
|
||||
* 매장의 오늘 매출 조회
|
||||
*
|
||||
* @param storeId 매장 ID
|
||||
* @return 오늘 매출
|
||||
*/
|
||||
@Query("SELECT COALESCE(SUM(s.salesAmount), 0) FROM Sales s WHERE s.storeId = :storeId AND s.salesDate = CURRENT_DATE")
|
||||
BigDecimal findTodaySalesByStoreId(@Param("storeId") Long storeId);
|
||||
|
||||
/**
|
||||
* 매장의 이번 달 매출 조회
|
||||
*
|
||||
* @param storeId 매장 ID
|
||||
* @return 이번 달 매출
|
||||
*/
|
||||
@Query("SELECT COALESCE(SUM(s.salesAmount), 0) FROM Sales s WHERE s.storeId = :storeId AND YEAR(s.salesDate) = YEAR(CURRENT_DATE) AND MONTH(s.salesDate) = MONTH(CURRENT_DATE)")
|
||||
BigDecimal findMonthSalesByStoreId(@Param("storeId") Long storeId);
|
||||
|
||||
/**
|
||||
* 매장의 전일 대비 매출 변화량 조회
|
||||
*
|
||||
* @param storeId 매장 ID
|
||||
* @return 전일 대비 매출 변화량
|
||||
*/
|
||||
@Query("SELECT COALESCE((SELECT SUM(s1.salesAmount) FROM Sales s1 WHERE s1.storeId = :storeId AND s1.salesDate = CURRENT_DATE) - (SELECT SUM(s2.salesAmount) FROM Sales s2 WHERE s2.storeId = :storeId AND s2.salesDate = CURRENT_DATE - 1), 0)")
|
||||
BigDecimal findPreviousDayComparisonByStoreId(@Param("storeId") Long storeId);
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.won.smarketing.store.repository;
|
||||
|
||||
import com.won.smarketing.store.entity.Store;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 매장 정보 데이터 접근을 위한 Repository
|
||||
* JPA를 사용한 매장 CRUD 작업 처리
|
||||
*/
|
||||
@Repository
|
||||
public interface StoreRepository extends JpaRepository<Store, Long> {
|
||||
|
||||
/**
|
||||
* 회원 ID로 매장 조회
|
||||
*
|
||||
* @param memberId 회원 ID
|
||||
* @return 매장 정보 (Optional)
|
||||
*/
|
||||
Optional<Store> findByMemberId(Long memberId);
|
||||
|
||||
/**
|
||||
* 회원의 매장 존재 여부 확인
|
||||
*
|
||||
* @param memberId 회원 ID
|
||||
* @return 존재 여부
|
||||
*/
|
||||
boolean existsByMemberId(Long memberId);
|
||||
|
||||
/**
|
||||
* 매장명으로 매장 조회
|
||||
*
|
||||
* @param storeName 매장명
|
||||
* @return 매장 목록
|
||||
*/
|
||||
Optional<Store> findByStoreName(String storeName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.won.smarketing.store.service;
|
||||
|
||||
import com.won.smarketing.store.dto.MenuCreateRequest;
|
||||
import com.won.smarketing.store.dto.MenuResponse;
|
||||
import com.won.smarketing.store.dto.MenuUpdateRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 메뉴 서비스 인터페이스
|
||||
* 메뉴 관리 관련 비즈니스 로직 정의
|
||||
*/
|
||||
public interface MenuService {
|
||||
|
||||
/**
|
||||
* 메뉴 등록
|
||||
*
|
||||
* @param request 메뉴 등록 요청 정보
|
||||
* @return 등록된 메뉴 정보
|
||||
*/
|
||||
MenuResponse register(MenuCreateRequest request);
|
||||
|
||||
/**
|
||||
* 메뉴 목록 조회
|
||||
*
|
||||
* @param category 메뉴 카테고리 (선택사항)
|
||||
* @return 메뉴 목록
|
||||
*/
|
||||
List<MenuResponse> getMenus(String category);
|
||||
|
||||
/**
|
||||
* 메뉴 정보 수정
|
||||
*
|
||||
* @param menuId 메뉴 ID
|
||||
* @param request 메뉴 수정 요청 정보
|
||||
* @return 수정된 메뉴 정보
|
||||
*/
|
||||
MenuResponse updateMenu(Long menuId, MenuUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 메뉴 삭제
|
||||
*
|
||||
* @param menuId 메뉴 ID
|
||||
*/
|
||||
void deleteMenu(Long menuId);
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package com.won.smarketing.store.service;
|
||||
|
||||
import com.won.smarketing.common.exception.BusinessException;
|
||||
import com.won.smarketing.common.exception.ErrorCode;
|
||||
import com.won.smarketing.store.dto.MenuCreateRequest;
|
||||
import com.won.smarketing.store.dto.MenuResponse;
|
||||
import com.won.smarketing.store.dto.MenuUpdateRequest;
|
||||
import com.won.smarketing.store.entity.Menu;
|
||||
import com.won.smarketing.store.repository.MenuRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 메뉴 관리 서비스 구현체
|
||||
* 메뉴 등록, 조회, 수정, 삭제 기능 구현
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class MenuServiceImpl implements MenuService {
|
||||
|
||||
private final MenuRepository menuRepository;
|
||||
|
||||
/**
|
||||
* 메뉴 정보 등록
|
||||
*
|
||||
* @param request 메뉴 등록 요청 정보
|
||||
* @return 등록된 메뉴 정보
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public MenuResponse register(MenuCreateRequest request) {
|
||||
// 메뉴 엔티티 생성 및 저장
|
||||
Menu menu = Menu.builder()
|
||||
.storeId(request.getStoreId())
|
||||
.menuName(request.getMenuName())
|
||||
.category(request.getCategory())
|
||||
.price(request.getPrice())
|
||||
.description(request.getDescription())
|
||||
.image(request.getImage())
|
||||
.build();
|
||||
|
||||
Menu savedMenu = menuRepository.save(menu);
|
||||
return toMenuResponse(savedMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 목록 조회
|
||||
*
|
||||
* @param category 메뉴 카테고리 (선택사항)
|
||||
* @return 메뉴 목록
|
||||
*/
|
||||
@Override
|
||||
public List<MenuResponse> getMenus(String category) {
|
||||
List<Menu> menus;
|
||||
|
||||
if (category != null && !category.trim().isEmpty()) {
|
||||
menus = menuRepository.findByCategoryOrderByMenuNameAsc(category);
|
||||
} else {
|
||||
menus = menuRepository.findAllByOrderByMenuNameAsc();
|
||||
}
|
||||
|
||||
return menus.stream()
|
||||
.map(this::toMenuResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 정보 수정
|
||||
*
|
||||
* @param menuId 수정할 메뉴 ID
|
||||
* @param request 메뉴 수정 요청 정보
|
||||
* @return 수정된 메뉴 정보
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public MenuResponse updateMenu(Long menuId, MenuUpdateRequest request) {
|
||||
Menu menu = menuRepository.findById(menuId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.MENU_NOT_FOUND));
|
||||
|
||||
// 메뉴 정보 업데이트
|
||||
menu.updateMenuInfo(
|
||||
request.getMenuName(),
|
||||
request.getCategory(),
|
||||
request.getPrice(),
|
||||
request.getDescription(),
|
||||
request.getImage()
|
||||
);
|
||||
|
||||
Menu updatedMenu = menuRepository.save(menu);
|
||||
return toMenuResponse(updatedMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* 메뉴 삭제
|
||||
*
|
||||
* @param menuId 삭제할 메뉴 ID
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteMenu(Long menuId) {
|
||||
Menu menu = menuRepository.findById(menuId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.MENU_NOT_FOUND));
|
||||
|
||||
menuRepository.delete(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu 엔티티를 MenuResponse DTO로 변환
|
||||
*
|
||||
* @param menu Menu 엔티티
|
||||
* @return MenuResponse DTO
|
||||
*/
|
||||
private MenuResponse toMenuResponse(Menu menu) {
|
||||
return MenuResponse.builder()
|
||||
.menuId(menu.getId())
|
||||
.menuName(menu.getMenuName())
|
||||
.category(menu.getCategory())
|
||||
.price(menu.getPrice())
|
||||
.description(menu.getDescription())
|
||||
.image(menu.getImage())
|
||||
.createdAt(menu.getCreatedAt())
|
||||
.updatedAt(menu.getUpdatedAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.won.smarketing.store.service;
|
||||
|
||||
import com.won.smarketing.store.dto.SalesResponse;
|
||||
|
||||
/**
|
||||
* 매출 서비스 인터페이스
|
||||
* 매출 조회 관련 비즈니스 로직 정의
|
||||
*/
|
||||
public interface SalesService {
|
||||
|
||||
/**
|
||||
* 매출 정보 조회
|
||||
*
|
||||
* @return 매출 정보
|
||||
*/
|
||||
SalesResponse getSales();
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.won.smarketing.store.service;
|
||||
|
||||
import com.won.smarketing.store.dto.SalesResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 매출 서비스 구현체
|
||||
* 매출 조회 기능 구현 (현재는 Mock 데이터)
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class SalesServiceImpl implements SalesService {
|
||||
|
||||
/**
|
||||
* 매출 정보 조회
|
||||
* 현재는 Mock 데이터를 반환 (실제로는 매출 데이터 조회 로직 필요)
|
||||
*
|
||||
* @return 매출 정보
|
||||
*/
|
||||
@Override
|
||||
public SalesResponse getSales() {
|
||||
log.info("매출 정보 조회");
|
||||
|
||||
// Mock 데이터 (실제로는 데이터베이스에서 조회)
|
||||
BigDecimal todaySales = new BigDecimal("150000");
|
||||
BigDecimal monthSales = new BigDecimal("4500000");
|
||||
BigDecimal yesterdaySales = new BigDecimal("125000");
|
||||
BigDecimal targetSales = new BigDecimal("176000");
|
||||
|
||||
// 전일 대비 변화 계산
|
||||
BigDecimal previousDayComparison = todaySales.subtract(yesterdaySales);
|
||||
BigDecimal previousDayChangeRate = yesterdaySales.compareTo(BigDecimal.ZERO) > 0
|
||||
? previousDayComparison.divide(yesterdaySales, 4, RoundingMode.HALF_UP)
|
||||
.multiply(new BigDecimal("100"))
|
||||
: BigDecimal.ZERO;
|
||||
|
||||
// 목표 대비 달성률 계산
|
||||
BigDecimal goalAchievementRate = targetSales.compareTo(BigDecimal.ZERO) > 0
|
||||
? todaySales.divide(targetSales, 4, RoundingMode.HALF_UP)
|
||||
.multiply(new BigDecimal("100"))
|
||||
: BigDecimal.ZERO;
|
||||
|
||||
return SalesResponse.builder()
|
||||
.todaySales(todaySales)
|
||||
.monthSales(monthSales)
|
||||
.previousDayComparison(previousDayComparison)
|
||||
.previousDayChangeRate(previousDayChangeRate)
|
||||
.goalAchievementRate(goalAchievementRate)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.won.smarketing.store.service;
|
||||
|
||||
import com.won.smarketing.store.dto.StoreCreateRequest;
|
||||
import com.won.smarketing.store.dto.StoreResponse;
|
||||
import com.won.smarketing.store.dto.StoreUpdateRequest;
|
||||
|
||||
/**
|
||||
* 매장 서비스 인터페이스
|
||||
* 매장 관리 관련 비즈니스 로직 정의
|
||||
*/
|
||||
public interface StoreService {
|
||||
|
||||
/**
|
||||
* 매장 등록
|
||||
*
|
||||
* @param request 매장 등록 요청 정보
|
||||
* @return 등록된 매장 정보
|
||||
*/
|
||||
StoreResponse register(StoreCreateRequest request);
|
||||
|
||||
/**
|
||||
* 매장 정보 조회 (현재 로그인 사용자)
|
||||
*
|
||||
* @return 매장 정보
|
||||
*/
|
||||
StoreResponse getMyStore();
|
||||
|
||||
/**
|
||||
* 매장 정보 조회 (매장 ID)
|
||||
*
|
||||
* @param storeId 매장 ID
|
||||
* @return 매장 정보
|
||||
*/
|
||||
StoreResponse getStore(String storeId);
|
||||
|
||||
/**
|
||||
* 매장 정보 수정
|
||||
*
|
||||
* @param storeId 매장 ID
|
||||
* @param request 매장 수정 요청 정보
|
||||
* @return 수정된 매장 정보
|
||||
*/
|
||||
StoreResponse updateStore(Long storeId, StoreUpdateRequest request);
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
package com.won.smarketing.store.service;
|
||||
|
||||
import com.won.smarketing.common.exception.BusinessException;
|
||||
import com.won.smarketing.common.exception.ErrorCode;
|
||||
import com.won.smarketing.store.dto.StoreCreateRequest;
|
||||
import com.won.smarketing.store.dto.StoreResponse;
|
||||
import com.won.smarketing.store.dto.StoreUpdateRequest;
|
||||
import com.won.smarketing.store.entity.Store;
|
||||
import com.won.smarketing.store.repository.StoreRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 매장 서비스 구현체
|
||||
* 매장 등록, 조회, 수정 기능 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class StoreServiceImpl implements StoreService {
|
||||
|
||||
private final StoreRepository storeRepository;
|
||||
|
||||
/**
|
||||
* 매장 등록
|
||||
*
|
||||
* @param request 매장 등록 요청 정보
|
||||
* @return 등록된 매장 정보
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public StoreResponse register(StoreCreateRequest request) {
|
||||
String currentUserId = getCurrentUserId();
|
||||
Long memberId = Long.valueOf(currentUserId); // 실제로는 Member ID 조회 필요
|
||||
|
||||
log.info("매장 등록 시작: {} (회원: {})", request.getStoreName(), memberId);
|
||||
|
||||
// 회원당 하나의 매장만 등록 가능
|
||||
if (storeRepository.existsByMemberId(memberId)) {
|
||||
throw new BusinessException(ErrorCode.STORE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
// 매장 엔티티 생성 및 저장
|
||||
Store store = Store.builder()
|
||||
.memberId(memberId)
|
||||
.storeName(request.getStoreName())
|
||||
.businessType(request.getBusinessType())
|
||||
.address(request.getAddress())
|
||||
.phoneNumber(request.getPhoneNumber())
|
||||
.businessHours(request.getBusinessHours())
|
||||
.closedDays(request.getClosedDays())
|
||||
.seatCount(request.getSeatCount())
|
||||
.snsAccounts(request.getSnsAccounts())
|
||||
.description(request.getDescription())
|
||||
.build();
|
||||
|
||||
Store savedStore = storeRepository.save(store);
|
||||
log.info("매장 등록 완료: {} (ID: {})", savedStore.getStoreName(), savedStore.getId());
|
||||
|
||||
return toStoreResponse(savedStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 매장 정보 조회 (현재 로그인 사용자)
|
||||
*
|
||||
* @return 매장 정보
|
||||
*/
|
||||
@Override
|
||||
public StoreResponse getMyStore() {
|
||||
String currentUserId = getCurrentUserId();
|
||||
Long memberId = Long.valueOf(currentUserId);
|
||||
|
||||
Store store = storeRepository.findByMemberId(memberId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.STORE_NOT_FOUND));
|
||||
|
||||
return toStoreResponse(store);
|
||||
}
|
||||
|
||||
/**
|
||||
* 매장 정보 조회 (매장 ID)
|
||||
*
|
||||
* @param storeId 매장 ID
|
||||
* @return 매장 정보
|
||||
*/
|
||||
@Override
|
||||
public StoreResponse getStore(String storeId) {
|
||||
try {
|
||||
Long id = Long.valueOf(storeId);
|
||||
Store store = storeRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.STORE_NOT_FOUND));
|
||||
|
||||
return toStoreResponse(store);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 매장 정보 수정
|
||||
*
|
||||
* @param storeId 매장 ID
|
||||
* @param request 매장 수정 요청 정보
|
||||
* @return 수정된 매장 정보
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public StoreResponse updateStore(Long storeId, StoreUpdateRequest request) {
|
||||
Store store = storeRepository.findById(storeId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.STORE_NOT_FOUND));
|
||||
|
||||
// 매장 정보 업데이트
|
||||
store.updateStore(
|
||||
request.getStoreName(),
|
||||
request.getBusinessType(),
|
||||
request.getAddress(),
|
||||
request.getPhoneNumber(),
|
||||
request.getBusinessHours(),
|
||||
request.getClosedDays(),
|
||||
request.getSeatCount(),
|
||||
request.getSnsAccounts(),
|
||||
request.getDescription()
|
||||
);
|
||||
|
||||
Store updatedStore = storeRepository.save(store);
|
||||
log.info("매장 정보 수정 완료: {} (ID: {})", updatedStore.getStoreName(), updatedStore.getId());
|
||||
|
||||
return toStoreResponse(updatedStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store 엔티티를 StoreResponse DTO로 변환
|
||||
*
|
||||
* @param store Store 엔티티
|
||||
* @return StoreResponse DTO
|
||||
*/
|
||||
private StoreResponse toStoreResponse(Store store) {
|
||||
return StoreResponse.builder()
|
||||
.storeId(store.getId())
|
||||
.storeName(store.getStoreName())
|
||||
.businessType(store.getBusinessType())
|
||||
.address(store.getAddress())
|
||||
.phoneNumber(store.getPhoneNumber())
|
||||
.businessHours(store.getBusinessHours())
|
||||
.closedDays(store.getClosedDays())
|
||||
.seatCount(store.getSeatCount())
|
||||
.snsAccounts(store.getSnsAccounts())
|
||||
.description(store.getDescription())
|
||||
.createdAt(store.getCreatedAt())
|
||||
.updatedAt(store.getUpdatedAt())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 로그인된 사용자 ID 조회
|
||||
*
|
||||
* @return 사용자 ID
|
||||
*/
|
||||
private String getCurrentUserId() {
|
||||
return SecurityContextHolder.getContext().getAuthentication().getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8082}
|
||||
servlet:
|
||||
context-path: /
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: store-service
|
||||
datasource:
|
||||
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:storedb}
|
||||
username: ${POSTGRES_USER:postgres}
|
||||
password: ${POSTGRES_PASSWORD:postgres}
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: ${JPA_DDL_AUTO:update}
|
||||
show-sql: ${JPA_SHOW_SQL:true}
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
format_sql: true
|
||||
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
operations-sorter: method
|
||||
api-docs:
|
||||
path: /api-docs
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.won.smarketing.store: ${LOG_LEVEL:DEBUG}
|
||||
Reference in New Issue
Block a user