Отчёт.

This commit is contained in:
Programmist73 2023-05-26 17:46:41 +04:00
parent 1bc1073b83
commit b2b505b9fb
19 changed files with 702 additions and 0 deletions

View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="Gradle: antlr:antlr:2.7.7">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/antlr/antlr/2.7.7/83cd2cd674a217ade95a4bb83a8a14f351f48bd0/antlr-2.7.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: ch.qos.logback:logback-classic:1.2.10">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.10/f69d97ef3335c6ab82fc21dfb77ac613f90c1221/logback-classic-1.2.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.10/734366e821747a955aa230ed8eb08b3cc4cfba2/logback-classic-1.2.10-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: ch.qos.logback:logback-core:1.2.10">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-core/1.2.10/5328406bfcae7bcdcc86810fcb2920d2c297170d/logback-core-1.2.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-core/1.2.10/63dc43165f77fedf926b2bcf72212f3755c87fba/logback-core-1.2.10-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.fasterxml:classmate:1.5.1">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml/classmate/1.5.1/3fe0bed568c62df5e89f4f174c101eab25345b6c/classmate-1.5.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml/classmate/1.5.1/504edac38ff03cc5ce1d0391abb1416ffad58a99/classmate-1.5.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.fasterxml.jackson.core:jackson-annotations:2.13.1">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.13.1/1cbcbe4623113e6af92ccaa89884a345270f1a87/jackson-annotations-2.13.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.13.1/7d167558a509af98d1ce5610ba142735a6990faa/jackson-annotations-2.13.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.1">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.13.1/fa89741b057f4f2094e2f115e6cf898626989fe2/jackson-dataformat-yaml-2.13.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.13.1/804218de6e90e4bf2e844a2475585afa87078de6/jackson-dataformat-yaml-2.13.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,46 @@
import React, {useState} from 'react';
import { useRoutes, Outlet, BrowserRouter } from 'react-router-dom';
import AddLevel from './components/AddLevel';
import MainHead from './components/MainHead';
import AddNation from './components/AddNation';
import AddTank from './components/AddTank';
import AddClient from './components/AddClient';
import PageForChecking from './components/PageForChecking';
function Router(props) {
return useRoutes(props.rootRoute);
}
function App() {
const routes = [
{ index: true, element: <AddLevel /> },
{ path: 'levels', element: <AddLevel />, label: 'Обзор уровней' },
{ path: 'nations', element: <AddNation />, label: 'Обзор наций' },
{ path: 'tanks', element: <AddTank />, label: 'Обзор танков'},
{ path: 'clients', element: <AddClient />, label: 'Обзор клиентов'},
{ path: 'checkPage', element: <PageForChecking />, label: 'Фильтр по танкам'}
];
const links = routes.filter(route => route.hasOwnProperty('label'));
const rootRoute = [
{ path: '/', element: render(links), children: routes }
];
function render(links) {
return (
<div className="App">
<MainHead links={links}/>
<Outlet/>
</div>
);
}
return (
<BrowserRouter>
<Router rootRoute={ rootRoute } />
</BrowserRouter>
);
}
export default App;

View File

@ -0,0 +1,37 @@
.client-card{
display: flex;
width: 100%;
padding: 15px;
margin-top: 5px;
border: 5px solid;
border-color: #14A76C;
border-radius: 10px;
justify-content: space-around;
align-items: center;
font-family: Courier, monospace;
font-weight: 900;
}
.client-attribute{
padding: 5px;
border-radius: 10px;
background-color: #FF652F;
font-family: Courier, monospace;
font-weight: 900;
align-items: center;
}
.client-button-group{
display: flex;
width: 20%;
justify-content: space-around;
align-items: center;
}
.client-button{
padding: 10px;
border-radius: 10px;
background-color: #FF652F;
font-family: Courier, monospace;
font-weight: 900;
}

View File

@ -0,0 +1,61 @@
import React, { useState } from 'react';
import axios from 'axios';
import './ClientItem.css';
import ModalClient from './ModalClient';
import ModalTankNation from '../Nation/ModalTankNation';
const ClientItem = (data) => {
const [client, setClient] = useState(null);
//состояние для контроля вызова модального окна
const[modal, setModal] = useState(false);
//состояние для вызова окна показа списка танков нации
const[modalNation, setModalNation] = useState(false);
function deleteClient(){
axios.delete('http://localhost:8080/client/' + data.clientItem.id)
.then((response) => {
console.log("Удаление уровня с id " + data.clientItem.id)
});
}
return (
<div className="client-card">
<p className="client-attribute"> id: {data.clientItem.id} </p>
<p className="client-attribute"> Никнейм: {data.clientItem.nickName} </p>
<p className="client-attribute"> Баланс: {data.clientItem.balance} </p>
<p className="client-attribute"> Почта: {data.clientItem.email} </p>
<div className='client-button-group'>
<button className="client-button" type="button"
onClick={() => setModal(true)}
>
Редактировать
</button>
<button className="client-button" type="button"
onClick={deleteClient}
>
Удалить
</button>
<button className="nation-button" type="button"
onClick={() => setModalNation(true)}
>
Список танков
</button>
<ModalClient
data={data.clientItem}
visible={modal}
setVisible={setModal}
/>
<ModalTankNation
data={data.clientItem}
visible={modalNation}
setVisible={setModalNation}
/>
</div>
</div>
);
};
export default ClientItem

View File

@ -0,0 +1,22 @@
import React, { useEffect} from 'react';
import ClientItem from './ClientItem';
const ClientList = (clients) => {
return (
<div>
<div>
<h1 style={{textAlign: 'center', fontFamily: 'courier, monospace', background: '#FF652F', borderRadius: '10px'}}>
Список существующих клиентов:
</h1>
</div>
{clients.clientItems.map((clientItem) =>
<ClientItem
clientItem={clientItem}
key={clientItem.id}
/>
)}
</div>
);
}
export default ClientList;

View File

@ -0,0 +1,90 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/*#505050*/
#root{
display: flex;
justify-content: center;
background-color: #151719;
background-image: url("../images/wot-is-4.jpg");
background-size: cover;
background-attachment: fixed;
font-family: Courier, monospace;
font-weight: 900;
}
.body{
margin: 0; /* Убираем отступы */
height: 100%; /* Высота страницы */
}
.App{
width: 1200px;
}
.Group_create_level{
display: flex;
padding: 15px;
background-color: #FFE430;
opacity: 0.8;
border-radius: 10px;
margin-top: 15px;
justify-content: space-around;
align-items: center;
}
.Card_list{
padding: 15px;
border: 5px solid;
border-color: #14A76C;
background-color: #151719;
opacity: 0.9;
border-radius: 10px;
margin-top: 15px;
align-items: center;
}
.Main_head{
display: flex;
padding: 15px;
margin-top: 30px;
border: 5px solid;
border-color: #FF652F;
border-radius: 10px;
background-color: #151719;
opacity: 0.9;
justify-content: space-between;
align-items: center;
}
.Button_Main_Group{
padding: 5px;
border-color: #FF652F;
border-radius: 10px;
background-color: #FF652F;
font-family: Courier, monospace;
font-weight: bold;
align-items: center;
}
.add-level-button{
padding: 10px;
margin-right: 15px;
}
.Main-label{
display: flex;
padding-top: 16px;
font-size: 7vw;
font-variant: small-caps;
font-stretch: ultra-expanded;
justify-content: space-around;
align-items: center;
color: #505050;
-webkit-text-stroke-width: 3.0px;
-webkit-text-stroke-color: #000000;
}

View File

@ -0,0 +1,44 @@
package premium_store.controller.DTO;
import premium_store.model.GameClient;
import java.util.List;
//класс, который соединяет танки клиента в одну строчку (нам так захотелось)
public class ClientDTO {
private final long id;
private final String nickName;
private final String email;
private final Integer balance;
private final List<TankDTO> tanks;
public ClientDTO(GameClient gameClient){
this.id = gameClient.getId();
this.nickName = gameClient.getNickName();
this.email = gameClient.getEmail();
this.balance = gameClient.getBalance();
this.tanks = gameClient.getTanks().stream()
.map(TankDTO::new)
.toList();
}
public long getId(){
return id;
}
public String getNickName(){
return nickName;
}
public String getEmail(){
return email;
}
public Integer getBalance(){
return balance;
}
public List<TankDTO> getTanks(){
return tanks;
}
}

View File

@ -0,0 +1,31 @@
package premium_store.controller.DTO;
import premium_store.model.Nation;
import java.util.List;
public class FullNationDTO {
public final Long id;
public final String nation;
public final List<TankDTO> tanks;
public FullNationDTO(Nation nation){
this.id = nation.getId();
this.nation = nation.getNation();
this.tanks = nation.getTanksOfThisNation().stream()
.map(TankDTO::new)
.toList();
}
public Long getId() {
return id;
}
public String getNation() {
return nation;
}
public List<TankDTO> getTanksOfThisNation() {
return tanks;
}
}

View File

@ -0,0 +1,55 @@
package premium_store.controller.controller;
import org.springframework.web.bind.annotation.*;
import premium_store.controller.DTO.ClientDTO;
import premium_store.service.GameClientService;
import premium_store.service.TankService;
import java.util.List;
@RestController
@RequestMapping("/client")
public class GameClientController {
private final GameClientService gameClientService;
private final TankService tankService;
public GameClientController(GameClientService gameClientService, TankService tankService){
this.gameClientService = gameClientService;
this.tankService = tankService;
}
//аннотация PathVariable связывает значения id из URL и Long id
@GetMapping("/{id}")
public ClientDTO getClient(@PathVariable Long id) {
return new ClientDTO(gameClientService.findClient(id));
}
//с помощью Java Stream преобразуем набор пришедших данных в объекты StudentDto
@GetMapping("/")
public List<ClientDTO> getClients() {
return gameClientService.findAllClients().stream()
.map(ClientDTO::new)
.toList();
}
@PostMapping("/")
public ClientDTO createClient(@RequestParam("nickName") String nickName,
@RequestParam("email") String email,
@RequestParam("balance") Integer balance) {
return new ClientDTO(gameClientService.addClient(nickName, email, balance));
}
@PutMapping("/{id}")
public ClientDTO updateClient(@PathVariable Long id,
@RequestParam("nickName") String nickName,
@RequestParam("email") String email,
@RequestParam("balance") Integer balance,
@RequestParam("tankId") Long tankId) {
return new ClientDTO(gameClientService.updateClient(id, nickName, email, balance, tankService.findTank(tankId)));
}
@DeleteMapping("/{id}")
public ClientDTO deleteClient(@PathVariable Long id) {
return new ClientDTO(gameClientService.deleteClient(id));
}
}

View File

@ -0,0 +1,103 @@
package premium_store.model;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Entity
@Table(name = "GAMECLIENTS")
public class GameClient {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "nickName", nullable = false, length = 255)
private String nickName;
@Column(name = "email", nullable = false, length = 255)
private String email;
@Column(name = "balance")
private Integer balance;
@OneToMany(fetch = FetchType.EAGER)
private List<Tank> tanks = new ArrayList<>();
public GameClient(){ }
public GameClient(String nickName, String email, Integer balance){
this.nickName = nickName;
this.email = email;
this.balance = balance;
}
public Long getId(){
return id;
}
public String getNickName(){
return nickName;
}
public void setNickName(String nickName){
this.nickName = nickName;
}
public String getEmail(){
return email;
}
public void setEmail(String email){
this.email = email;
}
public Integer getBalance(){
return balance;
}
public void setBalance(Integer balance){
this.balance = balance;
}
public List<Tank> getTanks(){
return tanks;
}
public void setTanks(Tank tank){
this.tanks.add(tank);
}
//метод для сравнения
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
GameClient gameClient = (GameClient) o;
return Objects.equals(id, gameClient.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
//преобразование данных по объекту в строчку
@Override
public String toString() {
return "Client{" +
"id=" + id +
", nickName='" + nickName + '\'' +
", email='" + email + '\'' +
", balance='" + balance + '\'' +
", tanks='" + tanks.stream().map(Object::toString).collect(Collectors.joining(", ")) + '\'' +
'}';
}
}

View File

@ -0,0 +1,7 @@
package premium_store.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import premium_store.model.GameClient;
public interface GameClientRepository extends JpaRepository<GameClient, Long> {
}

View File

@ -0,0 +1,85 @@
package premium_store.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import premium_store.model.GameClient;
import premium_store.model.Tank;
import premium_store.repository.GameClientRepository;
import premium_store.service.exception.ClientNotFoundException;
import premium_store.util.validation.ValidatorUtil;
import java.util.List;
import java.util.Optional;
@Service
public class GameClientService {
private final GameClientRepository gameClientRepository;
private final ValidatorUtil validatorUtil;
public GameClientService(GameClientRepository gameClientRepository, ValidatorUtil validatorUtil){
this.gameClientRepository = gameClientRepository;
this.validatorUtil = validatorUtil;
}
@Transactional
public GameClient addClient(String newNickName, String newEmail, Integer newBallance) {
final GameClient gameClient = new GameClient(newNickName, newEmail, newBallance);
validatorUtil.validate(gameClient);
return gameClientRepository.save(gameClient);
}
@Transactional(readOnly = true)
public GameClient findClient(Long id) {
final Optional<GameClient> client = gameClientRepository.findById(id);
return client.orElseThrow(() -> new ClientNotFoundException(id));
}
@Transactional(readOnly = true)
public List<GameClient> findAllClients() {
return gameClientRepository.findAll();
}
@Transactional
public GameClient updateClient(Long id, String newNickName, String newEmail, Integer newBalance, Tank newTank) {
if (id <= 0) {
throw new IllegalArgumentException("Invalid id");
}
final GameClient currentGameClient = findClient(id);
if (StringUtils.hasText(newNickName)){
currentGameClient.setNickName(newNickName);
}
if(StringUtils.hasText(newEmail)){
currentGameClient.setEmail(newEmail);
}
if(newBalance != null){
currentGameClient.setBalance(newBalance);
}
if(newTank != null){
currentGameClient.setTanks(newTank);
}
return gameClientRepository.save(currentGameClient);
}
@Transactional
public GameClient deleteClient(Long id) {
final GameClient currentGameClient = findClient(id);
gameClientRepository.delete(currentGameClient);
return currentGameClient;
}
//прямой sql-запрос на удаление всех записей в таблице
@Transactional
public void deleteAllClients() {
gameClientRepository.deleteAll();
}
}

View File

@ -0,0 +1,7 @@
package premium_store.service.exception;
public class ClientNotFoundException extends RuntimeException {
public ClientNotFoundException(Long id) {
super(String.format("Client with id [%s] is not found", id));
}
}

View File

@ -0,0 +1,50 @@
package premium_store.util.error;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import premium_store.service.exception.ClientNotFoundException;
import premium_store.service.exception.LevelNotFoundException;
import premium_store.service.exception.NationNotFoundException;
import premium_store.service.exception.TankNotFoundException;
import premium_store.util.validation.ValidationException;
import java.util.stream.Collectors;
//контроллер для обработки разнообразных ошибок при работе с запросами к БД
@ControllerAdvice
public class AdviceController {
//метод handleException будет вызываться при возникновении исключений типа LevelNotFoundException и т. д.
@ExceptionHandler({
LevelNotFoundException.class,
NationNotFoundException.class,
ClientNotFoundException.class,
TankNotFoundException.class,
ValidationException.class
})
public ResponseEntity<Object> handleException(Throwable e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
//при исключении автоматической валидации данных
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleBindException(MethodArgumentNotValidException e) {
final ValidationException validationException = new ValidationException(
e.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toSet()));
return handleException(validationException);
}
//обработка неизвестных ошибок
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleUnknownException(Throwable e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}