sehunbang 2024. 2. 1. 19:54

 

Scheduler

@Scheduled("{시간}")

CronExpression

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/support/CronExpression.html

 

 

CronExpression (Spring Framework 6.1.3 API)

Determine whether the given string represents a valid cron expression.

docs.spring.io

@Scheduled(cron = "0 0 1 * * *") // 매일 새벽 1시

 

// ( 초 , 분 , 시 , 일 , 달 , 년 )

//////

(spring data JPA)

PageAble 

(정렬 방법)

 

Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC; 

Sort sort = Sort.by(direction, sortBy);

PageAble pageable = PageRequest.of ( page, size , sort )

Page<Product> products = productRepository.findAllByUser(user, pageable);

 

@GetMapping("/products")
public Page<ProductResponseDto> getProducts(
        @RequestParam("page") int page
        ,
        @RequestParam("size") int size
        ,
        @RequestParam("sortBy") String sortBy
        ,
        @RequestParam("isAsc") boolean isAsc
        ,
        @AuthenticationPrincipal UserDetailsImpl userDetails){
    return productService.getProduct(userDetails.getUser()
    ,page-1 /*페이지는 클라는 1 부터 서버는 0부터 시작함*/
            ,size,sortBy,isAsc
    );
}

 

service

 

public Page<ProductResponseDto> getProduct(User user, int page, int size, String sortBy, boolean isAsc) {
    Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
    Sort sort = Sort.by(direction,sortBy);
    Pageable pageable = PageRequest.of(page,size,sort);
    // admin 인지 확인
    UserRoleEnum uenum = user.getRole();
    Page<Product> all;
    if(uenum == UserRoleEnum.USER){
       all = pr.findByUser(user,pageable);
    }
    else {
        all = pr.findAll(pageable);
    }
    return all.map(ProductResponseDto::new);
}

 

repo

public interface productRepository extends JpaRepository<Product,Long> {
    Page<Product> findByUser(User user, Pageable pageable);
}

 

 

 

Package Util -> TestRunner   (한번만 사용 하고 주석 처리 하세요)

 

inplements ApplicationRunner : Spring 이 처음 Run 할때 내부 코트들이 수행이 됨.

@Component
public class TestDataRunner implements ApplicationRunner {

    @Autowired
    UserService userService;
    @Autowired
    productRepository productRepository;
    @Autowired
    UserRepository userRepository;
    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    NaverApiService naverApiService;

    @Override
    public void run(ApplicationArguments args) {
        // 테스트 User 생성
        User testUser = new User("Robbie", passwordEncoder.encode("1234"), "robbie@sparta.com", UserRoleEnum.USER);
        testUser = userRepository.save(testUser);

        // 테스트 User 의 관심상품 등록
        // 검색어 당 관심상품 10개 등록
        createTestData(testUser, "신발");
        createTestData(testUser, "과자");
        createTestData(testUser, "키보드");
        createTestData(testUser, "휴지");
        createTestData(testUser, "휴대폰");
        createTestData(testUser, "앨범");
        createTestData(testUser, "헤드폰");
        createTestData(testUser, "이어폰");
        createTestData(testUser, "노트북");
        createTestData(testUser, "무선 이어폰");
        createTestData(testUser, "모니터");
    }

    private void createTestData(User user, String searchWord) {
        // 네이버 쇼핑 API 통해 상품 검색
        List<ItemDto> itemDtoList = naverApiService.searchItems(searchWord);

        List<Product> productList = new ArrayList<>();

        for (ItemDto itemDto : itemDtoList) {
            Product product = new Product();
            // 관심상품 저장 사용자
            product.setUser(user);
            // 관심상품 정보
            product.setTitle(itemDto.getTitle());
            product.setLink(itemDto.getLink());
            product.setImage(itemDto.getImage());
            product.setLprice(itemDto.getLprice());

            // 희망 최저가 랜덤값 생성
            // 최저 (100원) ~ 최대 (상품의 현재 최저가 + 10000원)
            int myPrice = getRandomNumber(min_price, itemDto.getLprice() + 10000);
            product.setMyprice(myPrice);

            productList.add(product);
        }

        productRepository.saveAll(productList);
    }

    public int getRandomNumber(int min, int max) {
        return (int) ((Math.random() * (max - min)) + min);
    }
}

 

 

 

폴더 만들기

 

Entity :

결과적으로 상품과 폴더는 N : M 다대다 관계입니다.

@ManyToMany 애너테이션을 사용하여 다대다 관계를 풀 수도 있지만 상품_폴더 중간 테이블을 직접 만들어 풀어보겠습니다.

 

Folder 

@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "folder")
public class Folder {

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

    @Column(nullable = false)
    private String name;

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

    public Folder(String name, User user) {
        this.name = name;
        this.user = user;
    }
}

 

Product  (양방향)

@OneToMany (mappedBy = "product")
private List<ProductFolder> productFolderlist = new ArrayList<>();

 

ProductFolder  (양방향)

@OneToMany(mappedBy = "product")
private List<ProductFolder> productFolderList = new ArrayList<>();

@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "product_folder")
public class ProductFolder {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id", nullable = false)
    private Product product;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "folder_id", nullable = false)
    private Folder folder;

    public ProductFolder(Product product, Folder folder) {
        this.product = product;
        this.folder = folder;
    }

 

Folder Repo

public interface FolderRepository extends JpaRepository<Folder,Long> {

}

 

 

 

생성 밑 조회 밑 구현 :

github 참조... + (복습예정)

 

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class FolderController {
    private final FolderService folderService;
    @PostMapping("/folders")
    public void addFolders(@RequestBody FolderRequestDto folderRequestDto,
                           @AuthenticationPrincipal UserDetailsImpl userDetails) {
        List<String> folderNames = folderRequestDto.getFolderNames();

        folderService.addFolders(folderNames, userDetails.getUser());
    }
    //
}

 //

@Service
@RequiredArgsConstructor
public class FolderService {
    private final FolderRepository folderRepository;

    // 로그인한 회원에 폴더들 등록
    public void addFolders(List<String> folderNames, User user) {

        // 입력으로 들어온 폴더 이름을 기준으로, 회원이 이미 생성한 폴더들을 조회합니다.
        List<Folder> existFolderList = folderRepository.findAllByUserAndNameIn(user, folderNames);

        List<Folder> folderList = new ArrayList<>();

        for (String folderName : folderNames) {
            // 이미 생성한 폴더가 아닌 경우만 폴더 생성
            if (!isExistFolderName(folderName, existFolderList)) {
                Folder folder = new Folder(folderName, user);
                folderList.add(folder);
            } else {
                throw new IllegalArgumentException("폴더명이 중복되었습니다.");
            }
        }

        folderRepository.saveAll(folderList);
    }
////
    //
public List<FolderResponseDto> getFolders(User user) {
    List<Folder> folderList = folderRepository.findAllByUser(user);
    List<FolderResponseDto> responseDtoList = new ArrayList<>();
    for (Folder folder : folderList) {
        responseDtoList.add(new FolderResponseDto(folder));
    }
    return responseDtoList;
}

    //

    //

    private Boolean isExistFolderName(String folderName, List<Folder> existFolderList) {
        // 기존 폴더 리스트에서 folder name 이 있는지?
        for (Folder existFolder : existFolderList) {
            if(folderName.equals(existFolder.getName())) {
                return true;
            }
        }
        return false;
    }
    //
}

 //

 

User-Controller

@GetMapping("/user-folder")
public String getUserInfo(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails){
    model.addAttribute("folders",folderService.getFolders(userDetails.getUser()));
    return "index :: #fragment";
}

 

 

Product response Dto

@Getter
@NoArgsConstructor
public class ProductResponseDto {
    private Long id;
    private String title;
    private String link;
    private String image;
    private int lprice;
    private int myprice;

    private List<FolderResponseDto> productFolderList = new ArrayList<>();
    public ProductResponseDto(Product product) {
        this.id = product.getId();
        this.title = product.getTitle();
        this.link = product.getLink();
        this.image = product.getImage();
        this.lprice = product.getLprice();
        this.myprice = product.getMyprice();
        for (ProductFolder productFolder : product.getProductFolderlist()) {
            productFolderList.add(new FolderResponseDto(productFolder.getFolder()));
        }
    }
}

 

 

Product Service 에 

@Transactional 추가 이유는 지연로딩 (영속성 상태 ) 때문에.

 

@Transactional(readOnly = true)
    public Page<ProductResponseDto> getProduct(User user, int page, int size, String sortBy, boolean isAsc) {
        Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction,sortBy);
        Pageable pageable = PageRequest.of(page,size,sort);
        // admin 인지 확인
        UserRoleEnum uenum = user.getRole();
        Page<Product> all;
        if(uenum == UserRoleEnum.USER){
           all = pr.findByUser(user,pageable);
        }
        else {
            all = pr.findAll(pageable);
        }
        return all.map(ProductResponseDto::new);
    }