initial commit

This commit is contained in:
skettiks
2026-03-03 17:34:10 +03:00
commit aa4f0dbf00
39 changed files with 1653 additions and 0 deletions
@@ -0,0 +1,13 @@
package com.project.movienight
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.runApplication
@SpringBootApplication
@ConfigurationPropertiesScan("com.project.movienight.config")
class MovieNightApplication
fun main(args: Array<String>) {
runApplication<MovieNightApplication>(*args)
}
@@ -0,0 +1,92 @@
package com.project.movienight
import com.project.movienight.application.ports.input.CreateFilmCommand
import com.project.movienight.application.ports.input.CreateUserCommand
import com.project.movienight.application.ports.input.EditFilmCommand
import com.project.movienight.application.ports.input.EditUserCommand
import com.project.movienight.application.services.FilmService
import com.project.movienight.application.services.UserService
import com.project.movienight.config.FilmServiceProperties
import com.project.movienight.config.UserServiceProperties
import org.springframework.boot.CommandLineRunner
import org.springframework.stereotype.Component
@Component
class MovieNightService(
private val userService: UserService,
private val userConfig: UserServiceProperties,
private val filmService: FilmService,
private val filmConfig: FilmServiceProperties,
) : CommandLineRunner {
fun performActions() {
println("Заблокированные имена пользователей: ${userConfig.blockedNames}")
println("Заблокированные паттерны в описании фильмов: ${filmConfig.blockedPatterns}")
println("Создание пользователей...")
var userAboba = userService.create(CreateUserCommand(name = "абоба", email = "aboba228@mail.com"))
println(userAboba)
val userAlice = userService.create(CreateUserCommand(name = "алиса", email = "alice@mail.com"))
println(userAlice)
val userBob = userService.create(CreateUserCommand(name = "боб", email = "bob@mail.com"))
println(userBob)
println("Создание фильмов...")
var filmSpiderMan =
filmService.create(
CreateFilmCommand(
"Spider-Man: Beyond the Spider-Verse",
"Spider-Man: Beyond the Spider-Verse is an upcoming American animated superhero film based on Marvel Comics featuring the character Miles Morales / Spider-Man.",
),
)
println(filmSpiderMan)
val filmStarWars =
filmService.create(
CreateFilmCommand(
"Star Wars: Episode III Revenge of the Sith",
"is a 2005 American epic space opera film written and directed by George Lucas. The sequel to Attack of the Clones (2002), it is the sixth film in the Star Wars film series.",
),
)
println(filmStarWars)
println("Редактирование фильма ${filmSpiderMan.title}")
filmSpiderMan = filmService.edit(filmSpiderMan.id, EditFilmCommand("Delayed", "-"))
println(filmSpiderMan)
println("Редактирование пользователя ${userAboba.name}")
userAboba = userService.edit(userAboba.id, EditUserCommand("абеме"))
println(userAboba)
println("Создание пользователя с запрещённым именем")
try {
userService.create(CreateUserCommand(name = "admin", email = "admin@mail.com"))
} catch (e: IllegalArgumentException) {
println("error: ${e.message}")
}
println("Создание фильмов с запрещённым паттернами")
try {
filmService.create(CreateFilmCommand("Python", "Python is the best language"))
} catch (e: IllegalArgumentException) {
println("error: ${e.message}")
}
println("Удаление пользователей")
userService.delete(userAboba.id)
userService.delete(userAlice.id)
userService.delete(userBob.id)
println("Удаление фильмов")
filmService.delete(filmSpiderMan.id)
filmService.delete(filmStarWars.id)
}
override fun run(vararg args: String) {
println("Запустился сервис")
performActions()
}
}
@@ -0,0 +1,10 @@
package com.project.movienight.adapters.id
import com.project.movienight.application.ports.output.IdGenerator
import org.springframework.stereotype.Component
import java.util.UUID
@Component
class UuidGenerator : IdGenerator {
override fun generateId(): UUID = UUID.randomUUID()
}
@@ -0,0 +1,35 @@
package com.project.movienight.adapters.persistence.inmemory
import com.project.movienight.application.ports.output.FilmLibraryRepositoryPort
import com.project.movienight.domain.model.FilmLibrary
import org.springframework.stereotype.Repository
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
@Repository
class FilmLibraryRepositoryAdapter : FilmLibraryRepositoryPort {
private val storage: MutableMap<UUID, FilmLibrary> = ConcurrentHashMap()
private val userIdIndex: MutableMap<UUID, UUID> = ConcurrentHashMap()
override fun save(filmLibrary: FilmLibrary): FilmLibrary {
storage[filmLibrary.id] = filmLibrary
userIdIndex[filmLibrary.userId] = filmLibrary.id
return filmLibrary
}
override fun findById(id: UUID): FilmLibrary? {
return storage[id]
}
override fun findAll(): List<FilmLibrary> {
return storage.values.toList()
}
override fun deleteById(id: UUID) {
val filmLibrary = storage[id]
if (filmLibrary != null) {
userIdIndex.remove(filmLibrary.userId)
}
storage.remove(id)
}
}
@@ -0,0 +1,26 @@
package com.project.movienight.adapters.persistence.inmemory
import com.project.movienight.application.ports.output.FilmRepositoryPort
import com.project.movienight.domain.model.Film
import org.springframework.stereotype.Repository
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
@Repository
class FilmRepositoryAdapter : FilmRepositoryPort {
private val films: MutableMap<UUID, Film> = ConcurrentHashMap()
override fun save(film: Film): Film {
films[film.id] = film
return film
}
override fun findById(id: UUID): Film? = films[id]
override fun findAll(): List<Film> = films.values.toList()
override fun deleteById(id: UUID) {
films.remove(id)
}
}
@@ -0,0 +1,26 @@
package com.project.movienight.adapters.persistence.inmemory
import com.project.movienight.application.ports.output.UserRepositoryPort
import com.project.movienight.domain.model.User
import org.springframework.stereotype.Repository
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
@Repository
class UserRepositoryAdapter : UserRepositoryPort {
private val users: MutableMap<UUID, User> = ConcurrentHashMap()
override fun save(user: User): User {
users[user.id] = user
return user
}
override fun findById(id: UUID): User? = users[id]
override fun findAll(): List<User> = users.values.toList()
override fun deleteById(id: UUID) {
users.remove(id)
}
}
@@ -0,0 +1,49 @@
package com.project.movienight.application.ports.input
import com.project.movienight.domain.model.FilmLibrary
import java.util.UUID
// 1. Создание бибилиотеки пользователя
interface CreateFilmLibraryUseCase {
fun create(command: CreateFilmLibraryCommand): FilmLibrary
}
data class CreateFilmLibraryCommand(
val userId: UUID,
val name: String = "Мои фильмы"
)
// 2. Добавление фильма в библиотеку
interface AddFilmToLibraryUseCase {
fun addFilm(command: AddFilmToLibraryCommand): FilmLibrary
}
data class AddFilmToLibraryCommand(
val userId: UUID,
val filmId: UUID
)
// 3. Удаление фильма из библиотеки
interface RemoveFilmFromLibraryUseCase {
fun removeFilm(command: RemoveFilmFromLibraryCommand): FilmLibrary
}
data class RemoveFilmFromLibraryCommand(
val userId: UUID,
val filmId: UUID,
val libraryId: UUID? = null
)
// 4. Получение библиотеки пользователя
interface GetFilmLibraryUseCase {
fun getLibrary(query: GetFilmLibraryQuery): FilmLibrary
}
data class GetFilmLibraryQuery(
val userId: UUID,
)
@@ -0,0 +1,29 @@
package com.project.movienight.application.ports.input
import com.project.movienight.domain.model.Film
import java.util.UUID
interface CreateFilmUseCase {
fun create(command: CreateFilmCommand): Film
}
data class CreateFilmCommand(
val title: String,
val description: String,
)
interface EditFilmUseCase {
fun edit(
id: UUID,
command: EditFilmCommand,
): Film
}
data class EditFilmCommand(
val title: String,
val description: String,
)
interface DeleteFilmUseCase {
fun delete(id: UUID)
}
@@ -0,0 +1,28 @@
package com.project.movienight.application.ports.input
import com.project.movienight.domain.model.User
import java.util.UUID
interface CreateUserUseCase {
fun create(command: CreateUserCommand): User
}
data class CreateUserCommand(
val name: String,
val email: String,
)
interface EditUserUseCase {
fun edit(
id: UUID,
command: EditUserCommand,
): User
}
data class EditUserCommand(
val name: String,
)
interface DeleteUserUseCase {
fun delete(id: UUID)
}
@@ -0,0 +1,14 @@
package com.project.movienight.application.ports.output
import com.project.movienight.domain.model.FilmLibrary
import java.util.UUID
interface FilmLibraryRepositoryPort {
fun save(filmLibrary: FilmLibrary): FilmLibrary
fun findById(id: UUID): FilmLibrary?
fun findAll(): List<FilmLibrary>
fun deleteById(id: UUID)
}
@@ -0,0 +1,14 @@
package com.project.movienight.application.ports.output
import com.project.movienight.domain.model.Film
import java.util.UUID
interface FilmRepositoryPort {
fun save(film: Film): Film
fun findById(id: UUID): Film?
fun findAll(): List<Film>
fun deleteById(id: UUID)
}
@@ -0,0 +1,7 @@
package com.project.movienight.application.ports.output
import java.util.UUID
interface IdGenerator {
fun generateId(): UUID
}
@@ -0,0 +1,14 @@
package com.project.movienight.application.ports.output
import com.project.movienight.domain.model.User
import java.util.UUID
interface UserRepositoryPort {
fun save(user: User): User
fun findById(id: UUID): User?
fun findAll(): List<User>
fun deleteById(id: UUID)
}
@@ -0,0 +1,63 @@
package com.project.movienight.application.services
import com.project.movienight.application.ports.input.CreateFilmCommand
import com.project.movienight.application.ports.input.CreateFilmUseCase
import com.project.movienight.application.ports.input.DeleteFilmUseCase
import com.project.movienight.application.ports.input.EditFilmCommand
import com.project.movienight.application.ports.input.EditFilmUseCase
import com.project.movienight.application.ports.output.FilmRepositoryPort
import com.project.movienight.application.ports.output.IdGenerator
import com.project.movienight.config.FilmServiceProperties
import com.project.movienight.domain.model.Film
import org.springframework.stereotype.Service
import java.util.UUID
@Service
class FilmService(
private val filmRepository: FilmRepositoryPort,
private val idGenerator: IdGenerator,
private val filmConfig: FilmServiceProperties,
) : CreateFilmUseCase,
EditFilmUseCase,
DeleteFilmUseCase {
override fun create(command: CreateFilmCommand): Film {
if (filmConfig.isBlocked(command.title)) {
throw IllegalArgumentException("Film with this title is not acceptable")
}
if (filmConfig.isBlocked(command.description)) {
throw IllegalArgumentException("Film with this description is not acceptable")
}
val film =
Film(
id = idGenerator.generateId(),
title = command.title,
description = command.description,
)
return filmRepository.save(film)
}
override fun edit(
id: UUID,
command: EditFilmCommand,
): Film {
if (filmConfig.isBlocked(command.title)) {
throw IllegalArgumentException("Film with this title is not acceptable")
}
if (filmConfig.isBlocked(command.description)) {
throw IllegalArgumentException("Film with this description is not acceptable")
}
var film = filmRepository.findById(id) ?: throw IllegalArgumentException("Film with id $id not found")
film = film.copy(title = command.title, description = command.description)
return filmRepository.save(film)
}
override fun delete(id: UUID) {
filmRepository.findById(id) ?: throw IllegalArgumentException("Film with id $id not found")
filmRepository.deleteById(id)
}
}
@@ -0,0 +1,58 @@
package com.project.movienight.application.services
import com.project.movienight.application.ports.input.CreateUserCommand
import com.project.movienight.application.ports.input.CreateUserUseCase
import com.project.movienight.application.ports.input.DeleteUserUseCase
import com.project.movienight.application.ports.input.EditUserCommand
import com.project.movienight.application.ports.input.EditUserUseCase
import com.project.movienight.application.ports.output.IdGenerator
import com.project.movienight.application.ports.output.UserRepositoryPort
import com.project.movienight.config.UserServiceProperties
import com.project.movienight.domain.model.User
import org.springframework.stereotype.Service
import java.util.UUID
@Service
class UserService(
private val userRepository: UserRepositoryPort,
private val idGenerator: IdGenerator,
private val userConfig: UserServiceProperties,
) : CreateUserUseCase,
EditUserUseCase,
DeleteUserUseCase {
override fun create(command: CreateUserCommand): User {
if (userConfig.isBlocked(command.name)) {
throw IllegalArgumentException("User with this name is not acceptable")
}
val user =
User(
id = idGenerator.generateId(),
name = command.name,
email = command.email,
library = null,
)
return userRepository.save(user)
}
override fun edit(
id: UUID,
command: EditUserCommand,
): User {
if (userConfig.isBlocked(command.name)) {
throw IllegalArgumentException("User with this name is not acceptable")
}
var user = userRepository.findById(id) ?: throw IllegalArgumentException("User with id $id not found")
user = user.copy(name = command.name)
return userRepository.save(user)
}
override fun delete(id: UUID) {
userRepository.findById(id) ?: throw IllegalArgumentException("User with id $id not found")
userRepository.deleteById(id)
}
}
@@ -0,0 +1,20 @@
package com.project.movienight.config
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties(prefix = "services.film")
data class FilmServiceProperties(
val blockedPatterns: List<String> = listOf(),
) {
val normalized: Set<String> =
blockedPatterns
.map { it.trim().lowercase() }
.filter { it.isNotBlank() }
.toSet()
fun isBlocked(fieldValue: String): Boolean {
val normalizedFieldValue = fieldValue.lowercase()
return normalized.any { pattern -> normalizedFieldValue.contains(pattern) }
}
}
@@ -0,0 +1,12 @@
package com.project.movienight.config
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties(prefix = "services.user")
data class UserServiceProperties(
val blockedNames: List<String> = listOf(),
) {
private val normalized: Set<String> = blockedNames.map { it.trim().lowercase() }.toSet()
fun isBlocked(name: String): Boolean = name.trim().lowercase() in normalized
}
@@ -0,0 +1,9 @@
package com.project.movienight.domain.model
import java.util.UUID
data class Film(
val id: UUID,
val title: String,
val description: String,
)
@@ -0,0 +1,8 @@
package com.project.movienight.domain.model
import java.util.UUID
data class FilmLibrary(
val id: UUID,
val userId: UUID,
)
@@ -0,0 +1,10 @@
package com.project.movienight.domain.model
import java.util.UUID
data class User(
val id: UUID,
val name: String,
val email: String,
val library: FilmLibrary?,
)
View File
+15
View File
@@ -0,0 +1,15 @@
spring:
application:
name: MovieNight
services:
user:
blocked-names:
- admin
- root
- system
film:
blocked-patterns:
- censored
- epstein
- python