сделал 4 лабу
This commit is contained in:
BIN
.vs/slnx.sqlite
BIN
.vs/slnx.sqlite
Binary file not shown.
BIN
.vs/ипРома/v17/.wsuo
Normal file
BIN
.vs/ипРома/v17/.wsuo
Normal file
Binary file not shown.
12
.vs/ипРома/v17/DocumentLayout.backup.json
Normal file
12
.vs/ипРома/v17/DocumentLayout.backup.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "C:\\Users\\busla\\Desktop\\\u0438\u043F\u0420\u043E\u043C\u0430\\",
|
||||
"Documents": [],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
"VerticalTabListWidth": 256,
|
||||
"DocumentGroups": []
|
||||
}
|
||||
]
|
||||
}
|
||||
12
.vs/ипРома/v17/DocumentLayout.json
Normal file
12
.vs/ипРома/v17/DocumentLayout.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "C:\\Users\\busla\\Desktop\\\u0438\u043F\u0420\u043E\u043C\u0430\\",
|
||||
"Documents": [],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
"VerticalTabListWidth": 256,
|
||||
"DocumentGroups": []
|
||||
}
|
||||
]
|
||||
}
|
||||
35
backend/build.front.gradle
Normal file
35
backend/build.front.gradle
Normal file
@@ -0,0 +1,35 @@
|
||||
apply plugin: "com.github.node-gradle.node"
|
||||
|
||||
logger.quiet("Configure front builder")
|
||||
|
||||
ext {
|
||||
frontDir = file("${project.projectDir}/front")
|
||||
logger.quiet("Webapp dir is ${frontDir}")
|
||||
}
|
||||
|
||||
node {
|
||||
version = "22.17.1"
|
||||
npmVersion = "10.9.2"
|
||||
download = true
|
||||
}
|
||||
|
||||
tasks.register("frontDepsInstall", NpmTask) {
|
||||
group = "front"
|
||||
description = "Installs dependencies from package.json"
|
||||
logger.quiet(description)
|
||||
workingDir = frontDir
|
||||
args = ["install"]
|
||||
}
|
||||
|
||||
tasks.register("frontBuild", NpmTask) {
|
||||
group = "front"
|
||||
description = "Build frontend webapp"
|
||||
logger.quiet(description)
|
||||
workingDir = frontDir
|
||||
dependsOn frontDepsInstall
|
||||
args = ["run", "build"]
|
||||
}
|
||||
|
||||
if (frontDir.exists()) {
|
||||
processResources.finalizedBy frontBuild
|
||||
}
|
||||
@@ -2,7 +2,10 @@ plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.5.5'
|
||||
id 'io.spring.dependency-management' version '1.1.7'
|
||||
id "com.github.node-gradle.node" version "7.1.0" apply false
|
||||
id 'org.liquibase.gradle' version '2.2.0'
|
||||
}
|
||||
|
||||
group = 'ru.ulstu.is'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
description = 'Movie API (Lab 1)'
|
||||
@@ -18,7 +21,49 @@ java {
|
||||
repositories { mavenCentral() }
|
||||
|
||||
ext {
|
||||
springdocVersion = '2.8.11'
|
||||
springProfiles = []
|
||||
|
||||
springdocVersion = '2.8.14'
|
||||
|
||||
springProfiles = []
|
||||
|
||||
if (project.hasProperty("front")) {
|
||||
springProfiles.add("front")
|
||||
}
|
||||
|
||||
if (project.hasProperty("prod")) {
|
||||
springProfiles.add("prod")
|
||||
} else {
|
||||
springProfiles.add("dev")
|
||||
}
|
||||
|
||||
currentProfiles = springProfiles.join(",")
|
||||
logger.quiet("Current profiles are: " + currentProfiles)
|
||||
}
|
||||
|
||||
liquibase {
|
||||
activities {
|
||||
dev {
|
||||
url 'jdbc:h2:file:./data/appdb'
|
||||
username 'sa'
|
||||
driver 'org.h2.Driver'
|
||||
changelogFile 'src/main/resources/db/changelog/db.changelog-master.yaml'
|
||||
}
|
||||
prod {
|
||||
url 'jdbc:postgresql://127.0.0.1:5432/internet_lab4'
|
||||
username 'postgres'
|
||||
password 'postgres'
|
||||
driver 'org.postgresql.Driver'
|
||||
changelogFile 'src/main/resources/db/changelog/db.changelog-master.yaml'
|
||||
}
|
||||
}
|
||||
runList = project.hasProperty('prod') ? 'prod' : 'dev'
|
||||
}
|
||||
|
||||
configurations {
|
||||
liquibaseRuntime {
|
||||
extendsFrom runtimeClasspath
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -26,10 +71,44 @@ dependencies {
|
||||
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${springdocVersion}"
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'org.liquibase:liquibase-core:4.30.0'
|
||||
|
||||
runtimeOnly 'com.h2database:h2'
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
|
||||
liquibaseRuntime 'org.liquibase:liquibase-core:4.30.0'
|
||||
liquibaseRuntime 'info.picocli:picocli:4.7.6'
|
||||
liquibaseRuntime 'com.h2database:h2:2.2.224'
|
||||
|
||||
if (springProfiles.contains("prod")) {
|
||||
runtimeOnly "org.postgresql:postgresql:42.7.4"
|
||||
} else {
|
||||
runtimeOnly "org.postgresql:postgresql:42.7.4"
|
||||
runtimeOnly "com.h2database:h2:2.2.224"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
if (springProfiles.contains("front")) {
|
||||
apply from: "build.front.gradle"
|
||||
}
|
||||
|
||||
|
||||
bootRun {
|
||||
def currentArgs = ["--spring.profiles.active=" + currentProfiles]
|
||||
if (project.hasProperty("args")) {
|
||||
currentArgs.addAll(project.args.tokenize())
|
||||
}
|
||||
args currentArgs
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
|
||||
systemProperty "spring.profiles.active", currentProfiles
|
||||
}
|
||||
Binary file not shown.
12
backend/data/appdb.trace.db
Normal file
12
backend/data/appdb.trace.db
Normal file
@@ -0,0 +1,12 @@
|
||||
2025-11-14 13:30:14.250845+04:00 jdbc[3]: exception
|
||||
org.h2.jdbc.JdbcSQLSyntaxErrorException: Таблица "DATABASECHANGELOGLOCK" не найдена
|
||||
Table "DATABASECHANGELOGLOCK" not found; SQL statement:
|
||||
SELECT COUNT(*) FROM PUBLIC.DATABASECHANGELOGLOCK [42102-224]
|
||||
2025-11-14 14:12:48.415553+04:00 jdbc[3]: exception
|
||||
org.h2.jdbc.JdbcSQLSyntaxErrorException: Таблица "DIRECTORS" уже существует
|
||||
Table "DIRECTORS" already exists; SQL statement:
|
||||
CREATE TABLE PUBLIC.DIRECTORS (ID BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, NAME VARCHAR(255) NOT NULL, CONSTRAINT CONSTRAINT_6 PRIMARY KEY (ID)) [42101-224]
|
||||
2025-11-14 14:17:18.481717+04:00 jdbc[3]: exception
|
||||
org.h2.jdbc.JdbcSQLSyntaxErrorException: Таблица "DATABASECHANGELOGLOCK" не найдена
|
||||
Table "DATABASECHANGELOGLOCK" not found; SQL statement:
|
||||
SELECT COUNT(*) FROM PUBLIC.DATABASECHANGELOGLOCK [42102-224]
|
||||
6
backend/package-lock.json
generated
Normal file
6
backend/package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@@ -6,6 +6,10 @@ import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.server.dto.MovieRq;
|
||||
import ru.ulstu.is.server.dto.MovieRs;
|
||||
import ru.ulstu.is.server.service.MovieService;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import ru.ulstu.is.server.dto.PageHelper;
|
||||
import ru.ulstu.is.server.dto.PageRs;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,10 +34,11 @@ public class MovieController {
|
||||
public MovieRs update(@PathVariable Long id, @RequestBody @Valid MovieRq rq) { return service.update(id, rq); }
|
||||
|
||||
@GetMapping("/page")
|
||||
public List<MovieRs> getPage(
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size) {
|
||||
return service.getPage(page, size);
|
||||
public PageRs<MovieRs> getPage(
|
||||
@RequestParam(defaultValue = "1") @Min(1) int page,
|
||||
@RequestParam(defaultValue = "12") @Min(1) int size) {
|
||||
Pageable pageable = PageHelper.toPageable(page, size);
|
||||
return service.getPage(pageable);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package ru.ulstu.is.server.api;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class SpaController {
|
||||
|
||||
@GetMapping("/")
|
||||
public String index() {
|
||||
return "forward:/index.html";
|
||||
}
|
||||
|
||||
@GetMapping("/{path:^(?!api$)(?!v3$)(?!swagger-ui$).*$}")
|
||||
public String any1() {
|
||||
return "forward:/index.html";
|
||||
}
|
||||
|
||||
@GetMapping("/{path:^(?!api$)(?!v3$)(?!swagger-ui$).*$}/**")
|
||||
public String any2() {
|
||||
return "forward:/index.html";
|
||||
}
|
||||
}
|
||||
15
backend/src/main/java/ru/ulstu/is/server/dto/PageHelper.java
Normal file
15
backend/src/main/java/ru/ulstu/is/server/dto/PageHelper.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package ru.ulstu.is.server.dto;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
public final class PageHelper {
|
||||
|
||||
private PageHelper() {
|
||||
}
|
||||
|
||||
public static Pageable toPageable(int page, int size) {
|
||||
return PageRequest.of(page - 1, size, Sort.by("id"));
|
||||
}
|
||||
}
|
||||
34
backend/src/main/java/ru/ulstu/is/server/dto/PageRs.java
Normal file
34
backend/src/main/java/ru/ulstu/is/server/dto/PageRs.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package ru.ulstu.is.server.dto;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public record PageRs<D>(
|
||||
List<D> items,
|
||||
int itemsCount,
|
||||
int currentPage,
|
||||
int currentSize,
|
||||
int totalPages,
|
||||
long totalItems,
|
||||
boolean isFirst,
|
||||
boolean isLast,
|
||||
boolean hasNext,
|
||||
boolean hasPrevious
|
||||
) {
|
||||
public static <D, E> PageRs<D> from(Page<E> page, Function<E, D> mapper) {
|
||||
return new PageRs<>(
|
||||
page.getContent().stream().map(mapper).toList(),
|
||||
page.getNumberOfElements(),
|
||||
page.getNumber() + 1,
|
||||
page.getSize(),
|
||||
page.getTotalPages(),
|
||||
page.getTotalElements(),
|
||||
page.isFirst(),
|
||||
page.isLast(),
|
||||
page.hasNext(),
|
||||
page.hasPrevious()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ import ru.ulstu.is.server.dto.MovieRq;
|
||||
import ru.ulstu.is.server.dto.MovieRs;
|
||||
import ru.ulstu.is.server.entity.Director;
|
||||
import ru.ulstu.is.server.entity.Genre;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import ru.ulstu.is.server.dto.PageHelper;
|
||||
import ru.ulstu.is.server.dto.PageRs;
|
||||
import ru.ulstu.is.server.entity.Movie;
|
||||
import ru.ulstu.is.server.mapper.MovieMapper;
|
||||
import ru.ulstu.is.server.repository.DirectorRepository;
|
||||
@@ -42,10 +45,8 @@ public class MovieService {
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<MovieRs> getPage(int page, int size) {
|
||||
return movies.findAll(PageRequest.of(page, size))
|
||||
.map(mapper::toRs)
|
||||
.toList();
|
||||
public PageRs<MovieRs> getPage(Pageable pageable) {
|
||||
return PageRs.from(movies.findAll(pageable), mapper::toRs);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
|
||||
19
backend/src/main/resources/application-dev.yml
Normal file
19
backend/src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:file:./data/appdb
|
||||
username: sa
|
||||
password:
|
||||
driver-class-name: org.h2.Driver
|
||||
|
||||
h2:
|
||||
console:
|
||||
enabled: true
|
||||
|
||||
jpa:
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
logging:
|
||||
level:
|
||||
org.springdoc: DEBUG
|
||||
4
backend/src/main/resources/application-front.yml
Normal file
4
backend/src/main/resources/application-front.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
spring:
|
||||
web:
|
||||
resources:
|
||||
add-mappings: true
|
||||
9
backend/src/main/resources/application-prod.yml
Normal file
9
backend/src/main/resources/application-prod.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://127.0.0.1:5432/internet_lab4
|
||||
username: postgres
|
||||
password: 1234
|
||||
driver-class-name: org.postgresql.Driver
|
||||
|
||||
jpa:
|
||||
show-sql: false
|
||||
@@ -1,18 +0,0 @@
|
||||
spring.main.banner-mode=off
|
||||
spring.application.name=server
|
||||
server.port=8080
|
||||
|
||||
spring.datasource.url=jdbc:h2:file:./data/appdb
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
|
||||
spring.h2.console.enabled=true
|
||||
spring.h2.console.path=/h2
|
||||
|
||||
# logging
|
||||
logging.level.ru.ulstu.is.server=DEBUG
|
||||
16
backend/src/main/resources/application.yml
Normal file
16
backend/src/main/resources/application.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
spring:
|
||||
liquibase:
|
||||
change-log: classpath:db/changelog/db.changelog-master.yaml
|
||||
application:
|
||||
name: server
|
||||
main:
|
||||
banner-mode: off
|
||||
profiles:
|
||||
active: dev
|
||||
jpa:
|
||||
open-in-view: false
|
||||
hibernate:
|
||||
ddl-auto: validate
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
@@ -0,0 +1,26 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: add-review-table
|
||||
author: busla
|
||||
changes:
|
||||
- createTable:
|
||||
tableName: reviews
|
||||
columns:
|
||||
- column:
|
||||
name: id
|
||||
type: BIGINT
|
||||
autoIncrement: true
|
||||
constraints:
|
||||
primaryKey: true
|
||||
nullable: false
|
||||
- column:
|
||||
name: movie_id
|
||||
type: BIGINT
|
||||
constraints:
|
||||
nullable: false
|
||||
- column:
|
||||
name: text
|
||||
type: VARCHAR(1000)
|
||||
- column:
|
||||
name: rating
|
||||
type: INT
|
||||
@@ -0,0 +1,334 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 1763112391251-1
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createTable:
|
||||
columns:
|
||||
- column:
|
||||
autoIncrement: true
|
||||
constraints:
|
||||
nullable: false
|
||||
primaryKey: true
|
||||
primaryKeyName: CONSTRAINT_6
|
||||
name: ID
|
||||
type: BIGINT
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: NAME
|
||||
type: VARCHAR(255)
|
||||
tableName: DIRECTORS
|
||||
- changeSet:
|
||||
id: 1763112391251-2
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createTable:
|
||||
columns:
|
||||
- column:
|
||||
autoIncrement: true
|
||||
constraints:
|
||||
nullable: false
|
||||
primaryKey: true
|
||||
primaryKeyName: CONSTRAINT_7
|
||||
name: ID
|
||||
type: BIGINT
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: NAME
|
||||
type: VARCHAR(255)
|
||||
tableName: GENRES
|
||||
- changeSet:
|
||||
id: 1763112391251-3
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createTable:
|
||||
columns:
|
||||
- column:
|
||||
autoIncrement: true
|
||||
constraints:
|
||||
nullable: false
|
||||
primaryKey: true
|
||||
primaryKeyName: CONSTRAINT_8
|
||||
name: ID
|
||||
type: BIGINT
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: GRADE
|
||||
type: REAL
|
||||
- column:
|
||||
name: IMAGE
|
||||
type: CHARACTER LARGE OBJECT
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: TITLE
|
||||
type: VARCHAR(255)
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: DIRECTOR_ID
|
||||
type: BIGINT
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: GENRE_ID
|
||||
type: BIGINT
|
||||
tableName: MOVIES
|
||||
- changeSet:
|
||||
id: 1763112391251-4
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createTable:
|
||||
columns:
|
||||
- column:
|
||||
autoIncrement: true
|
||||
constraints:
|
||||
nullable: false
|
||||
primaryKey: true
|
||||
primaryKeyName: CONSTRAINT_3
|
||||
name: ID
|
||||
type: BIGINT
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: LEVEL
|
||||
type: VARCHAR(255)
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: PRICE
|
||||
type: INT
|
||||
tableName: SUBSCRIPTIONS
|
||||
- changeSet:
|
||||
id: 1763112391251-5
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createTable:
|
||||
columns:
|
||||
- column:
|
||||
autoIncrement: true
|
||||
constraints:
|
||||
nullable: false
|
||||
primaryKey: true
|
||||
primaryKeyName: CONSTRAINT_4
|
||||
name: ID
|
||||
type: BIGINT
|
||||
- column:
|
||||
name: AGE
|
||||
type: INT
|
||||
- column:
|
||||
name: AVATAR
|
||||
type: CHARACTER LARGE OBJECT
|
||||
- column:
|
||||
name: GENDER
|
||||
type: VARCHAR(255)
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: IS_ADMIN
|
||||
type: BOOLEAN
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: LOGIN
|
||||
type: VARCHAR(255)
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: NAME
|
||||
type: VARCHAR(255)
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: PASSWORD
|
||||
type: VARCHAR(255)
|
||||
- column:
|
||||
name: SUBSCRIPTION_UNTIL
|
||||
type: date
|
||||
- column:
|
||||
name: SUBSCRIPTION_ID
|
||||
type: BIGINT
|
||||
tableName: USERS
|
||||
- changeSet:
|
||||
id: 1763112391251-6
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createTable:
|
||||
columns:
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: USER_ID
|
||||
type: BIGINT
|
||||
- column:
|
||||
constraints:
|
||||
nullable: false
|
||||
name: MOVIE_ID
|
||||
type: BIGINT
|
||||
tableName: USER_WATCHLIST
|
||||
- changeSet:
|
||||
id: 1763112391251-7
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addUniqueConstraint:
|
||||
columnNames: LEVEL
|
||||
constraintName: UKAVRYNEIAOOG6H0RWOC9QAHFGU
|
||||
tableName: SUBSCRIPTIONS
|
||||
- changeSet:
|
||||
id: 1763112391251-8
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addUniqueConstraint:
|
||||
columnNames: LOGIN
|
||||
constraintName: UKOW0GAN20590JRB00UPG3VA2FN
|
||||
tableName: USERS
|
||||
- changeSet:
|
||||
id: 1763112391251-9
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addUniqueConstraint:
|
||||
columnNames: NAME
|
||||
constraintName: UKPE1A9WOIK1K97L87CIEGUYHH4
|
||||
tableName: GENRES
|
||||
- changeSet:
|
||||
id: 1763112391251-10
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addUniqueConstraint:
|
||||
columnNames: NAME
|
||||
constraintName: UKT6U48CMKTDMYIEUC2B6Y4UMN
|
||||
tableName: DIRECTORS
|
||||
- changeSet:
|
||||
id: 1763112391251-11
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createIndex:
|
||||
associatedWith: ''
|
||||
columns:
|
||||
- column:
|
||||
name: USER_ID
|
||||
indexName: FK1A2SF5HA20F8A3SQGO3N4H3W6_INDEX_5
|
||||
tableName: USER_WATCHLIST
|
||||
- changeSet:
|
||||
id: 1763112391251-12
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createIndex:
|
||||
associatedWith: ''
|
||||
columns:
|
||||
- column:
|
||||
name: DIRECTOR_ID
|
||||
indexName: FK5FT3U8K962BMJD8RN2MR77J8D_INDEX_8
|
||||
tableName: MOVIES
|
||||
- changeSet:
|
||||
id: 1763112391251-13
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createIndex:
|
||||
associatedWith: ''
|
||||
columns:
|
||||
- column:
|
||||
name: MOVIE_ID
|
||||
indexName: FK9FHBUF2RH3U2D0CITGJX86ID9_INDEX_5
|
||||
tableName: USER_WATCHLIST
|
||||
- changeSet:
|
||||
id: 1763112391251-14
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createIndex:
|
||||
associatedWith: ''
|
||||
columns:
|
||||
- column:
|
||||
name: SUBSCRIPTION_ID
|
||||
indexName: FKFWX079XWW5UYFBPI9U8GWAM34_INDEX_4
|
||||
tableName: USERS
|
||||
- changeSet:
|
||||
id: 1763112391251-15
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- createIndex:
|
||||
associatedWith: ''
|
||||
columns:
|
||||
- column:
|
||||
name: GENRE_ID
|
||||
indexName: FKJP8FSY8A0KKMDI04I81V05C6A_INDEX_8
|
||||
tableName: MOVIES
|
||||
- changeSet:
|
||||
id: 1763112391251-16
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addForeignKeyConstraint:
|
||||
baseColumnNames: USER_ID
|
||||
baseTableName: USER_WATCHLIST
|
||||
constraintName: FK1A2SF5HA20F8A3SQGO3N4H3W6
|
||||
deferrable: false
|
||||
initiallyDeferred: false
|
||||
onDelete: RESTRICT
|
||||
onUpdate: RESTRICT
|
||||
referencedColumnNames: ID
|
||||
referencedTableName: USERS
|
||||
validate: true
|
||||
- changeSet:
|
||||
id: 1763112391251-17
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addForeignKeyConstraint:
|
||||
baseColumnNames: DIRECTOR_ID
|
||||
baseTableName: MOVIES
|
||||
constraintName: FK5FT3U8K962BMJD8RN2MR77J8D
|
||||
deferrable: false
|
||||
initiallyDeferred: false
|
||||
onDelete: RESTRICT
|
||||
onUpdate: RESTRICT
|
||||
referencedColumnNames: ID
|
||||
referencedTableName: DIRECTORS
|
||||
validate: true
|
||||
- changeSet:
|
||||
id: 1763112391251-18
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addForeignKeyConstraint:
|
||||
baseColumnNames: MOVIE_ID
|
||||
baseTableName: USER_WATCHLIST
|
||||
constraintName: FK9FHBUF2RH3U2D0CITGJX86ID9
|
||||
deferrable: false
|
||||
initiallyDeferred: false
|
||||
onDelete: RESTRICT
|
||||
onUpdate: RESTRICT
|
||||
referencedColumnNames: ID
|
||||
referencedTableName: MOVIES
|
||||
validate: true
|
||||
- changeSet:
|
||||
id: 1763112391251-19
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addForeignKeyConstraint:
|
||||
baseColumnNames: SUBSCRIPTION_ID
|
||||
baseTableName: USERS
|
||||
constraintName: FKFWX079XWW5UYFBPI9U8GWAM34
|
||||
deferrable: false
|
||||
initiallyDeferred: false
|
||||
onDelete: RESTRICT
|
||||
onUpdate: RESTRICT
|
||||
referencedColumnNames: ID
|
||||
referencedTableName: SUBSCRIPTIONS
|
||||
validate: true
|
||||
- changeSet:
|
||||
id: 1763112391251-20
|
||||
author: busla (generated)
|
||||
changes:
|
||||
- addForeignKeyConstraint:
|
||||
baseColumnNames: GENRE_ID
|
||||
baseTableName: MOVIES
|
||||
constraintName: FKJP8FSY8A0KKMDI04I81V05C6A
|
||||
deferrable: false
|
||||
initiallyDeferred: false
|
||||
onDelete: RESTRICT
|
||||
onUpdate: RESTRICT
|
||||
referencedColumnNames: ID
|
||||
referencedTableName: GENRES
|
||||
validate: true
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
databaseChangeLog:
|
||||
- include:
|
||||
file: db.changelog-001-initial.yaml
|
||||
relativeToChangelogFile: true
|
||||
- include:
|
||||
file: changeset-3-add-review.yaml
|
||||
relativeToChangelogFile: true
|
||||
@@ -1,8 +0,0 @@
|
||||
logging.level.ru.ulstu.is.server=DEBUG
|
||||
|
||||
spring.datasource.url=jdbc:h2:mem:testdb
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
spring.jpa.hibernate.ddl-auto=create-drop
|
||||
spring.jpa.show-sql=false
|
||||
10
backend/src/test/resources/application.yml
Normal file
10
backend/src/test/resources/application.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: create-drop
|
||||
|
||||
liquibase:
|
||||
enabled: false
|
||||
20303
front/package-lock.json
generated
20303
front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,40 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
export default function useMovies() {
|
||||
const [movies, setMovies] = useState([]);
|
||||
export default function useMovies(initialPage = 1, pageSize = 12) {
|
||||
const [page, setPage] = useState(initialPage);
|
||||
const [data, setData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [reload, setReload] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
axios.get("http://localhost:8080/api/1.0/movies")
|
||||
.then(res => setMovies(res.data))
|
||||
.catch(setError)
|
||||
setLoading(true);
|
||||
axios
|
||||
.get("http://localhost:8080/api/1.0/movies/page", {
|
||||
params: { page, size: pageSize },
|
||||
})
|
||||
.then((res) => setData(res.data))
|
||||
.catch((err) => setError(err))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
}, [page, pageSize, reload]);
|
||||
|
||||
const remove = async (id) => {
|
||||
try {
|
||||
await axios.delete(`http://localhost:8080/api/1.0/movies/${id}`);
|
||||
setMovies(prev => prev.filter(m => m.id !== id));
|
||||
setReload((r) => r + 1);
|
||||
} catch (err) {
|
||||
console.error("Ошибка при удалении:", err);
|
||||
}
|
||||
};
|
||||
|
||||
return { movies, loading, error, remove };
|
||||
return {
|
||||
pageData: data,
|
||||
loading,
|
||||
error,
|
||||
page,
|
||||
setPage,
|
||||
pageSize,
|
||||
remove,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,56 @@
|
||||
import useMovies from "../hooks/useMovies";
|
||||
import MovieCard from "../components/MovieCard";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function Movies() {
|
||||
const { movies, loading, error, remove } = useMovies();
|
||||
const {
|
||||
pageData,
|
||||
loading,
|
||||
error,
|
||||
page,
|
||||
setPage,
|
||||
remove,
|
||||
} = useMovies(1, 12);
|
||||
|
||||
if (loading) return <p className="text-center py-5">Загрузка…</p>;
|
||||
if (error) return <p className="text-danger py-5 text-center">Ошибка: {error.message}</p>;
|
||||
if (loading) {
|
||||
return <p className="text-center py-5">Загрузка…</p>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<p className="text-danger py-5 text-center">
|
||||
Ошибка: {error.message}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
if (!pageData || pageData.items.length === 0) {
|
||||
return (
|
||||
<div className="container py-4">
|
||||
<div className="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 className="h3 m-0">Каталог фильмов</h1>
|
||||
<Link to="/new" className="btn btn-success">
|
||||
+ Добавить
|
||||
</Link>
|
||||
</div>
|
||||
<p>Фильмы не найдены.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { items, totalPages, hasNext, hasPrevious } = pageData;
|
||||
|
||||
const goPrev = () => {
|
||||
if (hasPrevious && page > 1) {
|
||||
setPage(page - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const goNext = () => {
|
||||
if (hasNext && page < totalPages) {
|
||||
setPage(page + 1);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container py-4">
|
||||
@@ -18,13 +62,36 @@ export default function Movies() {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="row g-4">
|
||||
{movies.map((m) => (
|
||||
<div className="row g-4 mb-4">
|
||||
{items.map((m) => (
|
||||
<div className="col-6 col-md-4 col-lg-3" key={m.id}>
|
||||
<MovieCard movie={m} onDelete={remove} />
|
||||
<MovieCard movie={m} onDelete={() => remove(m.id)} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{}
|
||||
<div className="d-flex justify-content-center align-items-center gap-3">
|
||||
<button
|
||||
className="btn btn-outline-secondary"
|
||||
onClick={goPrev}
|
||||
disabled={!hasPrevious}
|
||||
>
|
||||
← Назад
|
||||
</button>
|
||||
|
||||
<span>
|
||||
Страница {page} из {totalPages}
|
||||
</span>
|
||||
|
||||
<button
|
||||
className="btn btn-outline-secondary"
|
||||
onClick={goNext}
|
||||
disabled={!hasNext}
|
||||
>
|
||||
Вперёд →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
// @ts-ignore
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
proxy: {
|
||||
'/movies': 'http://localhost:3000',
|
||||
'/genres': 'http://localhost:3000',
|
||||
'/directors': 'http://localhost:3000',
|
||||
'/users': 'http://localhost:3000',
|
||||
'/subscriptions': 'http://localhost:3000',
|
||||
'/purchases': 'http://localhost:3000',
|
||||
},
|
||||
build: {
|
||||
sourcemap: true,
|
||||
emptyOutDir: true,
|
||||
outDir: "../build/resources/main/static",
|
||||
},
|
||||
});
|
||||
|
||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "ипРома",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
Reference in New Issue
Block a user