Skip to content

Commit 371ed9d

Browse files
feat: start implementing service layer
1 parent a72c301 commit 371ed9d

16 files changed

Lines changed: 676 additions & 49 deletions

src/main/java/com/vianavitor/simplelibrarygame/dto/utils/UserIdAndHashPasswd.java renamed to src/main/java/com/vianavitor/simplelibrarygame/dto/utils/UserInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.vianavitor.simplelibrarygame.dto.utils;
22

3-
public record UserIdAndHashPasswd(
3+
public record UserInfo(
44
Long id,
55
String password
66
) {

src/main/java/com/vianavitor/simplelibrarygame/model/Author.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public class Author {
99
@Id
1010
@GeneratedValue(strategy = GenerationType.IDENTITY)
1111
private Long id;
12+
13+
@Column(unique = true)
1214
private String name;
1315

1416
@ManyToMany(mappedBy = "bookAuthors")

src/main/java/com/vianavitor/simplelibrarygame/model/Book.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ public class Book {
1818
@Nullable
1919
private String synopsis;
2020

21-
@ManyToMany
21+
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
2222
@JoinTable(
2323
name = "book_author",
2424
joinColumns = @JoinColumn(name = "book_id"),
2525
inverseJoinColumns = @JoinColumn(name = "author_id")
2626
)
2727
private Set<Author> bookAuthors = new HashSet<>();
2828

29-
@ManyToMany
29+
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
3030
@JoinTable(
3131
name = "book_genre",
3232
joinColumns = @JoinColumn(name = "book_id"),
@@ -37,8 +37,8 @@ public class Book {
3737
@Nullable
3838
private String imagePath;
3939

40-
@Nullable
41-
private String contentPath;
40+
// @Nullable
41+
// private String contentPath;
4242
private LocalDate releasedAt;
4343

4444
@Column(columnDefinition = "int not null default 0")
@@ -77,11 +77,11 @@ public void setTitle(String title) {
7777
}
7878

7979
@Nullable
80-
public String getDescription() {
80+
public String getSynopsis() {
8181
return synopsis;
8282
}
8383

84-
public void setDescription(@Nullable String synopsis) {
84+
public void setSynopsis(@Nullable String synopsis) {
8585
this.synopsis = synopsis;
8686
}
8787

@@ -110,14 +110,14 @@ public void setImagePath(@Nullable String imagePath) {
110110
this.imagePath = imagePath;
111111
}
112112

113-
@Nullable
114-
public String getContentPath() {
115-
return contentPath;
116-
}
117-
118-
public void setContentPath(@Nullable String contentPath) {
119-
this.contentPath = contentPath;
120-
}
113+
// @Nullable
114+
// public String getContentPath() {
115+
// return contentPath;
116+
// }
117+
//
118+
// public void setContentPath(@Nullable String contentPath) {
119+
// this.contentPath = contentPath;
120+
// }
121121

122122
public LocalDate getReleasedAt() {
123123
return releasedAt;

src/main/java/com/vianavitor/simplelibrarygame/model/utils/classes/User.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public abstract class User {
1313
@Id
1414
@GeneratedValue(strategy = GenerationType.IDENTITY)
1515
private Long id;
16+
private String name;
1617

1718
@Column(unique = true)
1819
private String username;
@@ -40,6 +41,14 @@ public void setId(Long id) {
4041
this.id = id;
4142
}
4243

44+
public String getName() {
45+
return name;
46+
}
47+
48+
public void setName(String name) {
49+
this.name = name;
50+
}
51+
4352
public String getUsername() {
4453
return username;
4554
}

src/main/java/com/vianavitor/simplelibrarygame/repository/AuthorRepository.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
import org.springframework.stereotype.Repository;
66

77
import java.util.List;
8+
import java.util.Optional;
89

910
// TODO: implements auxiliary classes that gives support to the bidirectional relationship
1011
@Repository
1112
public interface AuthorRepository extends CrudRepository<Author, Long> {
12-
List<Author> findByName(String name);
13+
Optional<Author> findByName(String name);
1314
}

src/main/java/com/vianavitor/simplelibrarygame/repository/BookRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
import org.springframework.data.repository.CrudRepository;
55
import org.springframework.stereotype.Repository;
66

7+
import java.util.List;
8+
79
// TODO: implements auxiliary classes that gives support to the bidirectional relationship
810
@Repository
911
public interface BookRepository extends CrudRepository<Book, Long> {
12+
boolean existsByTitle(String title);
1013

14+
List<Book> findByTitle(String title);
1115
}

src/main/java/com/vianavitor/simplelibrarygame/repository/GenreRepository.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.vianavitor.simplelibrarygame.model.Genre;
44
import org.springframework.stereotype.Repository;
55

6+
import java.util.Collection;
67
import java.util.List;
78
import java.util.Optional;
89

@@ -14,5 +15,5 @@ public interface GenreRepository extends org.springframework.data.repository.Rep
1415

1516
Optional<Genre> findById(Long id);
1617

17-
List<Genre> findByName(String name);
18+
Optional<Genre> findByName(String name);
1819
}

src/main/java/com/vianavitor/simplelibrarygame/repository/aux/LoggableUser.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package com.vianavitor.simplelibrarygame.repository.aux;
22

3-
import com.vianavitor.simplelibrarygame.dto.utils.UserIdAndHashPasswd;
3+
import com.vianavitor.simplelibrarygame.dto.utils.UserInfo;
44
import com.vianavitor.simplelibrarygame.model.utils.classes.User;
55

6+
import java.util.List;
67
import java.util.Optional;
78

89
public interface LoggableUser {
910
// used to get the user's hashed password based on the unique username
10-
Optional<UserIdAndHashPasswd> getIdAndPasswordByUsername(String username);
11+
Optional<UserInfo> getIdAndPasswordByUsername(String username);
1112

12-
Optional<User> findByUsername(String username);
13+
// Optional<User> findByUsername(String username);
1314

1415
boolean existsByUsername(String username);
1516

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.vianavitor.simplelibrarygame.service;
2+
3+
import com.vianavitor.simplelibrarygame.dto.utils.UserInfo;
4+
import com.vianavitor.simplelibrarygame.repository.AdministratorRepository;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.security.crypto.password.PasswordEncoder;
7+
import org.springframework.stereotype.Service;
8+
9+
@Service
10+
public class AdministratorService {
11+
@Autowired
12+
private AdministratorRepository repository;
13+
14+
@Autowired
15+
private PasswordEncoder encoder;
16+
17+
public Long login(String username, String password) {
18+
UserInfo userInfo = repository.getIdAndPasswordByUsername(username)
19+
.orElseThrow(() -> new RuntimeException("invalid username or password"));
20+
21+
boolean invalidPassword = encoder.matches(password, userInfo.password());
22+
23+
if (invalidPassword) {
24+
throw new RuntimeException("invalid username or password");
25+
}
26+
27+
boolean wasUserDeactivated = repository.getActiveById(userInfo.id());
28+
if (wasUserDeactivated) {
29+
throw new RuntimeException("this user was deactivated, talk with a professor or administrador to get more information");
30+
}
31+
32+
// TODO: create JWT Token for authentication
33+
// ...
34+
35+
return userInfo.id();
36+
}
37+
38+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.vianavitor.simplelibrarygame.service;
2+
3+
import com.vianavitor.simplelibrarygame.model.Author;
4+
import com.vianavitor.simplelibrarygame.model.Book;
5+
import com.vianavitor.simplelibrarygame.model.Genre;
6+
import com.vianavitor.simplelibrarygame.repository.AuthorRepository;
7+
import com.vianavitor.simplelibrarygame.repository.BookRepository;
8+
import com.vianavitor.simplelibrarygame.repository.GenreRepository;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.stereotype.Service;
11+
12+
import java.security.NoSuchAlgorithmException;
13+
import java.util.*;
14+
15+
@Service
16+
public class BookService {
17+
18+
@Autowired
19+
private BookRepository repository;
20+
21+
@Autowired
22+
private AuthorRepository authorRepository;
23+
24+
@Autowired
25+
private GenreRepository genreRepository;
26+
27+
private static final Map<Long, Book> cache = new HashMap<>();
28+
29+
public void add(Book newBook, boolean confirmed) {
30+
if (!confirmed) {
31+
boolean exists = repository.existsByTitle(newBook.getTitle());
32+
if (exists) {
33+
throw new RuntimeException("""
34+
there is a book with the same title registered, do you still want to proceed?
35+
""");
36+
}
37+
}
38+
39+
Set<Author> bookAuthors = this.getBookAuthorsHelper(newBook.getBookAuthors());
40+
Set<Genre> bookGenres = this.getBookGenresHelper(newBook.getBookGenres());
41+
42+
newBook.setBookGenres(bookGenres);
43+
newBook.setBookAuthors(bookAuthors);
44+
45+
// TODO: implement an IA to classify the reading difficulty
46+
// ...
47+
Book book = repository.save(newBook);
48+
}
49+
50+
public void changeImage() {
51+
// TODO: make it functional
52+
}
53+
54+
public void rate(Long id, int rate) {
55+
Book book = repository.findById(id)
56+
.orElseThrow(() -> new RuntimeException("not found book"));
57+
58+
double value = book.getRatingValue();
59+
int count = book.getRatingCount();
60+
61+
double avg = value + (rate - value)/ (count+1);
62+
63+
book.setRatingCount(count + 1);
64+
book.setRatingValue(avg);
65+
66+
repository.save(book);
67+
}
68+
69+
public void setAvailable(Long id, boolean value) {
70+
Book book = repository.findById(id)
71+
.orElseThrow(() -> new RuntimeException("not found book"));
72+
73+
book.setAvailable(value);
74+
repository.save(book);
75+
}
76+
77+
public void modify(Long id, Book data, boolean confirmed) {
78+
// TODO: implement a more efficient way to stores a cache to deal with no long accessed data
79+
Book book = cache.containsKey(id)
80+
? cache.remove(id)
81+
: repository.findById(id)
82+
.orElseThrow(() -> new RuntimeException("not found book"));
83+
84+
if (!cache.containsKey(id)) {
85+
cache.put(id, book);
86+
}
87+
88+
boolean isTitleFilledIn = data.getTitle() != null && !data.getTitle().isBlank();
89+
90+
// check if the title is duplicate and the librarian hadn't confirmed the action
91+
if (isTitleFilledIn && !confirmed) {
92+
List<Book> results = repository.findByTitle(data.getTitle());
93+
94+
if (!results.isEmpty()) {
95+
// if the search returns something it means that this tittle already belongs to another book registered
96+
Optional<Book> duplicateBookTitle = results.stream()
97+
.filter(b -> !Objects.equals(b.getId(), id))
98+
.findFirst();
99+
100+
if (duplicateBookTitle.isPresent()) {
101+
throw new RuntimeException("""
102+
there is a book with the same title registered, even so do you wish to proceed?
103+
""");
104+
}
105+
}
106+
}
107+
108+
if (isTitleFilledIn) {
109+
book.setTitle(data.getTitle());
110+
}
111+
if (data.getSynopsis() != null) {
112+
book.setSynopsis(data.getSynopsis());
113+
}
114+
if (data.getPageCount() > 0) {
115+
book.setPageCount(data.getPageCount());
116+
}
117+
if (data.getReleasedAt() != null) {
118+
book.setReleasedAt(data.getReleasedAt());
119+
}
120+
if (data.getBookAuthors() != null) {
121+
Set<Author> bookAuthors = this.getBookAuthorsHelper(data.getBookAuthors());
122+
book.setBookAuthors(bookAuthors);
123+
}
124+
if (data.getBookGenres() != null) {
125+
Set<Genre> bookGenres = this.getBookGenresHelper(data.getBookGenres());
126+
book.setBookGenres(bookGenres);
127+
}
128+
129+
repository.save(book);
130+
}
131+
132+
private Set<Author> getBookAuthorsHelper(Set<Author> bookAuthors) {
133+
Set<Author> result = new HashSet<>();
134+
135+
bookAuthors.forEach(currentAuthor -> {
136+
Author author = authorRepository
137+
.findByName(currentAuthor.getName())
138+
.orElseGet(() ->
139+
// if there aren't any currentAuthor registered with this name, then register a new one
140+
authorRepository.save(currentAuthor)
141+
);
142+
result.add(author);
143+
});
144+
145+
return result;
146+
}
147+
148+
private Set<Genre> getBookGenresHelper(Set<Genre> bookGenres) {
149+
Set<Genre> result = new HashSet<>();
150+
151+
bookGenres.forEach(currentGenre -> {
152+
genreRepository
153+
.findByName(currentGenre.getName())
154+
.ifPresent((genre) -> {
155+
result.add(genre);
156+
});
157+
});
158+
159+
return result;
160+
}
161+
}

0 commit comments

Comments
 (0)