-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Функциональность «Общие фильмы». #13
Changes from 46 commits
49e36c7
fb23b56
64caaca
a052ee1
2f84fdd
cf48bfb
c1b9153
8b549dd
9ab12f5
4783c65
7c8f4cf
b311651
3c41f3d
aa85a1f
cc72524
c64df2d
aab721a
1ea1dc3
6921722
5b11817
6c411aa
2fc382a
2592f5b
3047e39
5f2f5f5
9d4cecf
69fc158
81c47a0
1395559
353fcf8
d6ce899
f4b5e18
6e6813f
54b39fe
d2de999
c0a096b
0a194d7
08f4044
de4f098
8efe735
59f1783
8e15aa2
196e6fa
55226c2
6d437de
1cf30fd
eb17e62
722ea42
dcc6e71
1d5a347
2cd0944
98a3d20
ae2b7be
8a72284
701e81b
2a1dc27
8844abd
2ae3b38
e617e53
3b79221
6f693d3
26ac111
94166cb
9a5d460
28c1d67
8e8d8a3
341d62b
ca851c0
f65d0f9
7d8f437
2f57998
cea22e1
56a95dc
8961325
f9f76e3
85262cd
bbaf3db
bede5bd
2138933
74dea04
18997e3
fb5a940
155a2b2
b14c72f
9daf150
c7e8cb0
2da3bc0
747fc30
f64bd8d
eee2fb5
0babcb7
5604cdb
060d430
d9306f0
3da9f77
d864974
3edfc80
3969fb4
16e01ee
15e048e
f7b8069
a06bd92
e90a394
e3a0a74
054f655
439a1b1
7ecb974
2926829
958704f
beebd65
def7997
4d1cea8
70d374f
d7dedd4
1eab287
e4f9596
fc1e242
9e02089
e8285c1
4e00d15
daabff3
6d2691b
c7a2035
1db8e6d
f268b03
c860ab1
4fd9b7e
40ebb6b
051d3dc
6dea36e
eb59e2b
25e7e86
d4da16f
f760b04
3b01b62
e85bc8d
49cf73a
1573821
57df230
5143a1b
001122f
eb8a580
57925e0
1dc80d4
55693b2
bfad941
3423a26
d909221
a27e02e
69894fa
d833d00
dccdb39
e1d4e98
d6740bf
f271f6c
35a870d
4766475
eeb1ecc
52341fe
dd4fa95
1f3d51f
dce6ca3
d70c4d4
21f727e
30f9251
c8aa291
1348fba
54a4274
2f5bd2e
1b960c8
8d5b9bc
8c5ee35
f513ece
6c6b7c9
e93ee85
9e246b0
3ac1aa6
dd382c0
5e46f83
61474a2
56ee826
7aafefb
9cf064c
ba99643
62f63c9
47f842f
5d4e2a3
ab14a96
5514d80
84b982e
9bdf2d7
209243d
13d2c6a
95a0cdc
f03310b
57d6a84
564f7c5
7b96901
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package ru.yandex.practicum.filmorate.controller; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.*; | ||
import ru.yandex.practicum.filmorate.dto.ReviewDto; | ||
import ru.yandex.practicum.filmorate.service.ReviewService; | ||
|
||
import javax.validation.Valid; | ||
import java.util.List; | ||
|
||
@RestController | ||
@RequestMapping("/reviews") | ||
@RequiredArgsConstructor | ||
public class ReviewController { | ||
|
||
private final ReviewService reviewService; | ||
|
||
@PostMapping | ||
@ResponseStatus(HttpStatus.CREATED) | ||
public ReviewDto addReview(@Valid @RequestBody ReviewDto reviewDto) { | ||
return reviewService.addReview(reviewDto); | ||
} | ||
|
||
@PutMapping | ||
public ReviewDto updateReview(@Valid @RequestBody ReviewDto updatedReviewDto) { | ||
return reviewService.updateReview(updatedReviewDto); | ||
} | ||
|
||
@DeleteMapping("/{id}") | ||
public void deleteReview(@PathVariable long id) { | ||
reviewService.deleteReview(id); | ||
} | ||
|
||
@GetMapping("/{id}") | ||
public ReviewDto getReviewById(@PathVariable long id) { | ||
return reviewService.getReviewById(id); | ||
} | ||
|
||
@GetMapping | ||
public List<ReviewDto> getReviewsByFilmId(@RequestParam(required = false) Long filmId, | ||
@RequestParam(required = false, defaultValue = "10") int count) { | ||
return reviewService.getReviewsByFilmId(filmId, count); | ||
} | ||
|
||
@PutMapping("/{id}/like/{userId}") | ||
public ReviewDto addLikeToReview(@PathVariable long id, @PathVariable long userId) { | ||
return reviewService.addLikeToReview(id, userId); | ||
} | ||
|
||
@PutMapping("/{id}/dislike/{userId}") | ||
public ReviewDto addDislikeToReview(@PathVariable long id, @PathVariable long userId) { | ||
return reviewService.addDislikeToReview(id, userId); | ||
} | ||
|
||
@DeleteMapping("/{id}/like/{userId}") | ||
public ReviewDto deleteLikeFromReview(@PathVariable long id, @PathVariable long userId) { | ||
return reviewService.deleteLikeFromReview(id, userId); | ||
} | ||
|
||
@DeleteMapping("/{id}/dislike/{userId}") | ||
public ReviewDto deleteDislikeFromReview(@PathVariable long id, @PathVariable long userId) { | ||
return reviewService.deleteDislikeFromReview(id, userId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package ru.yandex.practicum.filmorate.dao; | ||
|
||
public interface ReviewLikeStorage { | ||
void add(long reviewId, long userId, String type); | ||
|
||
void delete(long reviewId, long userId, String type); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package ru.yandex.practicum.filmorate.dao; | ||
|
||
import ru.yandex.practicum.filmorate.model.Review; | ||
|
||
import java.util.List; | ||
|
||
public interface ReviewStorage extends Dao<Review> { | ||
List<Review> findByFilmIdLimitBy(long filmId, int count); | ||
|
||
List<Review> findAllLimitBy(int count); | ||
|
||
void addLikeToReview(long id); | ||
|
||
void addDislikeToReview(long id); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.dao.DataAccessException; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.core.RowMapper; | ||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
import org.springframework.jdbc.support.KeyHolder; | ||
import org.springframework.stereotype.Repository; | ||
|
@@ -192,4 +193,40 @@ private Collection<Film> extractToFilmList(ResultSet rs) throws SQLException, Da | |
|
||
return filmIdMap.values(); | ||
} | ||
|
||
@Override | ||
public List<Film> findFilmsByIds(Set<Long> filmIds) { | ||
if (filmIds.isEmpty()) { | ||
return Collections.emptyList(); | ||
} | ||
|
||
String placeholders = String.join(",", Collections.nCopies(filmIds.size(), "?")); | ||
|
||
String sql = "SELECT f.ID, f.TITLE, f.DESCRIPTION, f.RELEASE_DATE, f.DURATION, f.MPA_ID, " + | ||
"m.RATING_NAME, COUNT(fl.USER_ID) AS LIKES " + | ||
"FROM FILM f " + | ||
"LEFT JOIN MPA m ON f.MPA_ID = m.ID " + | ||
"LEFT JOIN film_like fl ON f.ID = fl.FILM_ID " + | ||
"WHERE f.ID IN (" + placeholders + ") " + | ||
"GROUP BY f.ID, m.RATING_NAME " + | ||
"ORDER BY LIKES DESC"; | ||
|
||
Object[] idsArray = filmIds.toArray(new Object[0]); | ||
|
||
return jdbcTemplate.query(sql, idsArray, new RowMapper<Film>() { | ||
@Override | ||
public Film mapRow(ResultSet rs, int rowNum) throws SQLException { | ||
Film film = new Film(); | ||
film.setId(rs.getLong("ID")); | ||
film.setName(rs.getString("TITLE")); | ||
film.setDescription(rs.getString("DESCRIPTION")); | ||
film.setReleaseDate(rs.getDate("RELEASE_DATE").toLocalDate()); | ||
film.setDuration(rs.getInt("DURATION")); | ||
film.setMpa(new Mpa(rs.getInt("MPA_ID"), rs.getString("RATING_NAME"))); | ||
film.setLikes(rs.getLong("LIKES")); | ||
return film; | ||
} | ||
}); | ||
Comment on lines
+257
to
+272
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Попробуй использовать query с немого другой сигнатурой: query(String sql, RowMapper<?> rm, Object ... args). Тогда не нужна будет 214 строка и в качестве аргумента можно использовать filmIds.toArray(). Получится лаконичнее) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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,119 @@ | ||
package ru.yandex.practicum.filmorate.dao.impl; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.dao.EmptyResultDataAccessException; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
import org.springframework.jdbc.support.KeyHolder; | ||
import org.springframework.stereotype.Repository; | ||
import ru.yandex.practicum.filmorate.dao.ReviewStorage; | ||
import ru.yandex.practicum.filmorate.exception.NotFoundException; | ||
import ru.yandex.practicum.filmorate.model.Review; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class ReviewDbStorage implements ReviewStorage { | ||
|
||
private final JdbcTemplate jdbcTemplate; | ||
|
||
@Override | ||
public Review add(final Review review) { | ||
final KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
final String sql = "INSERT INTO review (review_content, is_positive, useful, user_id, film_id) VALUES (?, ?, ?, ?, ?)"; | ||
jdbcTemplate.update(con -> { | ||
PreparedStatement stmt = con.prepareStatement(sql, new String[]{"id"}); | ||
stmt.setString(1, review.getContent()); | ||
stmt.setBoolean(2, review.isPositive()); | ||
stmt.setLong(3, review.getUseful()); | ||
stmt.setLong(4, review.getUserId()); | ||
stmt.setLong(5, review.getFilmId()); | ||
return stmt; | ||
}, keyHolder); | ||
|
||
review.setReviewId(Objects.requireNonNull(keyHolder.getKey(), "Не удалось добавить отзыв.").longValue()); | ||
|
||
return review; | ||
} | ||
|
||
@Override | ||
public void remove(final long id) { | ||
final String sql = "DELETE FROM review WHERE id = ?"; | ||
int update = jdbcTemplate.update(sql, id); | ||
if (update != 1) { | ||
throw new NotFoundException("Отзыв с id '" + id + "' не найден."); | ||
} | ||
} | ||
|
||
@Override | ||
public void update(final Review review) { | ||
final String sql = "UPDATE review SET review_content = ?, is_positive = ? WHERE id = ?"; | ||
final int update = jdbcTemplate.update(sql, review.getContent(), review.isPositive(), review.getReviewId()); | ||
if (update != 1) { | ||
throw new NotFoundException("Отзыв с id '" + review.getReviewId() + "' не найден."); | ||
} | ||
} | ||
|
||
@Override | ||
public Collection<Review> findAll() { | ||
final String sql = "SELECT id, review_content, is_positive, useful, user_id, film_id " + | ||
"FROM review ORDER BY useful DESC, id"; | ||
return jdbcTemplate.query(sql, this::mapReview); | ||
} | ||
|
||
@Override | ||
public Review findById(final long id) { | ||
final String sql = "SELECT id, review_content, is_positive, useful, user_id, film_id " + | ||
"FROM review WHERE id = ?"; | ||
try { | ||
return jdbcTemplate.queryForObject(sql, this::mapReview, id); | ||
} catch (EmptyResultDataAccessException e) { | ||
throw new NotFoundException("Отзыв с id '" + id + "' не найден."); | ||
} | ||
} | ||
|
||
@Override | ||
public List<Review> findByFilmIdLimitBy(final long filmId, final int count) { | ||
final String sql = "SELECT id, review_content, is_positive, useful, user_id, film_id " + | ||
"FROM review WHERE film_id = ? ORDER BY useful DESC, id LIMIT ?"; | ||
return jdbcTemplate.query(sql, this::mapReview, filmId, count); | ||
} | ||
|
||
@Override | ||
public List<Review> findAllLimitBy(final int count) { | ||
final String sql = "SELECT id, review_content, is_positive, useful, user_id, film_id " + | ||
"FROM review ORDER BY useful DESC, id LIMIT ?"; | ||
return jdbcTemplate.query(sql, this::mapReview, count); | ||
} | ||
|
||
@Override | ||
public void addLikeToReview(final long id) { | ||
final String sql = "UPDATE review SET useful = useful + 1 WHERE id = ?"; | ||
jdbcTemplate.update(sql, id); | ||
} | ||
|
||
@Override | ||
public void addDislikeToReview(long id) { | ||
final String sql = "UPDATE review SET useful = useful - 1 WHERE id = ?"; | ||
jdbcTemplate.update(sql, id); | ||
} | ||
|
||
private Review mapReview(final ResultSet rs, final int rowNum) throws SQLException { | ||
return Review.builder() | ||
.reviewId(rs.getLong("id")) | ||
.content(rs.getString("review_content")) | ||
.isPositive(rs.getBoolean("is_positive")) | ||
.useful(rs.getLong("useful")) | ||
.userId(rs.getLong("user_id")) | ||
.filmId(rs.getLong("film_id")) | ||
.build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package ru.yandex.practicum.filmorate.dao.impl; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.stereotype.Repository; | ||
import ru.yandex.practicum.filmorate.dao.ReviewLikeStorage; | ||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class ReviewLikeDbStorage implements ReviewLikeStorage { | ||
|
||
private final JdbcTemplate jdbcTemplate; | ||
|
||
@Override | ||
public void add(final long reviewId, final long userId, final String type) { | ||
final String sql = "INSERT INTO review_like VALUES (?, ?, ?)"; | ||
jdbcTemplate.update(sql, reviewId, userId, type); | ||
} | ||
|
||
@Override | ||
public void delete(final long reviewId, final long userId, final String type) { | ||
final String sql = "DELETE FROM review_like WHERE review_id = ? AND user_id = ? AND like_type = ?"; | ||
jdbcTemplate.update(sql, userId, type); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package ru.yandex.practicum.filmorate.dto; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
import javax.validation.constraints.NotBlank; | ||
import javax.validation.constraints.NotNull; | ||
|
||
@Data | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Builder | ||
public class ReviewDto { | ||
private long reviewId; | ||
@NotBlank(message = "Содержание отзыва не может быть пустым.") | ||
private String content; | ||
@NotNull(message = "Не указана полезность отзыва.") | ||
private Boolean isPositive; | ||
private long useful; | ||
@NotNull(message = "Не указан идентификатор пользователя.") | ||
private Long userId; | ||
@NotNull(message = "Не указан идентификатор фильма.") | ||
private Long filmId; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Здесь можно использовать String.format, чтобы соединить два стринга
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
можно, но это менее безопасно так как это может привести к sql инъекциям, по этому я оставлю свое решение
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Видимо ты меня неправильно понял. Я предложил тебе также присоединить твой placeholders, поэтому в плане инъекции безопасность не поменяется. Просто немного лаконичнее смотрится, если речь не идет о производительности. В случае одной переменной это не так критично, можешь оставить свой вариант)