Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package Application.Models.Entities;

import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.*;

Expand All @@ -18,11 +19,13 @@ public class BankAccount {
private Double balance;

@Column(name = "userlogin", nullable = false)
@Setter
private String userLogin;

@ManyToOne
@Setter
@JoinColumn(name = "user_id", nullable = false)
@JsonIgnore
private User user;

public BankAccount(User user) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package Application.Models.Entities;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;
import lombok.*;
import Application.Models.Enums.HairColor;
Expand Down Expand Up @@ -35,6 +36,7 @@ public class User {

@OneToMany(mappedBy = "user", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}
, orphanRemoval = true, fetch = FetchType.EAGER)
@JsonManagedReference
private List<BankAccount> bankAccounts = new ArrayList<>();

@ManyToMany(fetch = FetchType.EAGER)
Expand Down
21 changes: 17 additions & 4 deletions Presentation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,13 @@
</properties>

<dependencies>
<!-- Зависимость для тестирования посредством фреймворка JUnit. -->

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>


<!-- Зависимость для использования Lombok. -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
Expand Down Expand Up @@ -70,6 +66,23 @@
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>3.3.4</version>
</dependency>

<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.13</artifactId>
<version>4.0.0</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>

<!-- Блок для плагинов, которые будут запущены во время фаз сборки проекта. -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import DataAccess.Services.Interfaces.IUserService;
import Presentation.Interfaces.IBaseController;
import Presentation.Controllers.BaseController;
import Presentation.Kafka.Services.KafkaProducerService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
Expand All @@ -26,8 +27,8 @@ public BaseController baseController(
IUserManager userManager,
IUserService userService,
IBankAccountService bankAccountService,
IOperationService operationService) {
return new BaseController(userManager, userService, bankAccountService, operationService);
IOperationService operationService, KafkaProducerService kafkaProducerService) {
return new BaseController(userManager, userService, bankAccountService, operationService, kafkaProducerService);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package Presentation.Controllers;

import Application.Models.Entities.BankAccount;
import Application.Models.Entities.User;
import Application.ResultTypes.BankAccountResult;
import Presentation.DTO.BankAccountDTO;
import Presentation.DTO.CreateBankAccountDTO;
import Presentation.Interfaces.IBaseController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand All @@ -15,13 +17,13 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/bankaccounts")
@RequestMapping(value = "/bankaccounts", produces = "application/json")
public class BankAccountDTOController {

private final IBaseController baseController;


@Autowired
public BankAccountDTOController(IBaseController baseController) {
this.baseController = baseController;
Expand All @@ -32,7 +34,7 @@ public BankAccountDTOController(IBaseController baseController) {
@ApiResponse(responseCode = "200", description = "Счет найден", content = @Content(mediaType = "application/json", schema = @Schema(implementation = BankAccountDTO.class))),
@ApiResponse(responseCode = "404", description = "Счет не найден")
})
@GetMapping("/{id}")
@GetMapping(value = "/{id}")
public ResponseEntity<BankAccountDTO> getBankAccountById(@Parameter(description = "ID счета") @PathVariable int id) {
BankAccount account = baseController.GetBankAccountById(id);
if (account == null) {
Expand All @@ -47,12 +49,23 @@ public ResponseEntity<BankAccountDTO> getBankAccountById(@Parameter(description
@ApiResponse(responseCode = "400", description = "Некорректные данные")
})
@PostMapping(consumes = "application/json", produces = "application/json")
public ResponseEntity<?> createBankAccount(@RequestBody BankAccount account) {
public ResponseEntity<?> createBankAccount(@RequestBody CreateBankAccountDTO dto) {
try {
BankAccount account = new BankAccount();
account.setId(null);
BankAccountResult result = baseController.AddBankAccount(account.getUser().getId(), account);
account.setBalance(dto.getBalance());

User user = baseController.GetUserById(dto.getUserId());
if (user == null) {
return ResponseEntity.badRequest().body("User not found with ID: " + dto.getUserId());
}

account.setUser(user);
account.setUserLogin(user.getLogin());

BankAccountResult result = baseController.AddBankAccount(user.getId(), account);
if (result instanceof BankAccountResult.Success) {
return ResponseEntity.status(HttpStatus.CREATED).body(result);
return ResponseEntity.status(HttpStatus.CREATED).body(new BankAccountDTO(account));
} else {
return ResponseEntity.badRequest().body(result);
}
Expand All @@ -63,39 +76,37 @@ public ResponseEntity<?> createBankAccount(@RequestBody BankAccount account) {
}



@Operation(summary = "Обновить данные счета", description = "Обновляет данные о счете по ID")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Счет обновлен", content = @Content(mediaType = "application/json", schema = @Schema(implementation = BankAccountDTO.class))),
@ApiResponse(responseCode = "404", description = "Счет не найден"),
@ApiResponse(responseCode = "400", description = "Некорректные данные")
})
@PutMapping("/{id}")
@PutMapping(value = "/{id}", consumes = "application/json", produces = "application/json")
public ResponseEntity<?> updateBankAccount(@Parameter(description = "ID счета") @PathVariable int id,
@RequestBody BankAccount bankAccount) {
@RequestBody BankAccount account) {
BankAccount existingAccount = baseController.GetBankAccountById(id);
if (existingAccount == null) {
return ResponseEntity.notFound().build();
}

existingAccount.setBalance(bankAccount.getBalance());
existingAccount.setUser(bankAccount.getUser());
existingAccount.setBalance(account.getBalance());
existingAccount.setUser(account.getUser());

BankAccountResult result = baseController.UpdateBankAccount(existingAccount);
if (result instanceof BankAccountResult.Success) {
return ResponseEntity.ok(new BankAccountDTO(existingAccount));
} else {
return ResponseEntity.badRequest().body(result);
return ResponseEntity.status(HttpStatus.CREATED).body(new BankAccountDTO(account));
}
}


@Operation(summary = "Удалить счет", description = "Удаляет счет по ID")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "Счет удален"),
@ApiResponse(responseCode = "404", description = "Счет не найден")
})
@DeleteMapping("/{id}")
@DeleteMapping(value = "/{id}")
public ResponseEntity<?> deleteBankAccount(@Parameter(description = "ID счета") @PathVariable int id) {
BankAccount account = baseController.GetBankAccountById(id);
if (account == null) {
Expand All @@ -105,7 +116,7 @@ public ResponseEntity<?> deleteBankAccount(@Parameter(description = "ID счет
if (result instanceof BankAccountResult.Success) {
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
return ResponseEntity.status(HttpStatus.CREATED).body(new BankAccountDTO(account));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import DataAccess.Services.Interfaces.IBankAccountService;
import DataAccess.Services.Interfaces.IOperationService;
import DataAccess.Services.Interfaces.IUserService;
import Presentation.Kafka.Services.KafkaProducerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand All @@ -26,28 +27,36 @@ public class BaseController implements IBaseController {
private final IUserService userService;
private final IBankAccountService bankAccountService;
private final IOperationService operationService;
private final KafkaProducerService kafkaProducerService;

@Autowired
public BaseController(IUserManager userManager, IUserService userService, IBankAccountService bankAccountService, IOperationService operationService) {
public BaseController(IUserManager userManager, IUserService userService, IBankAccountService bankAccountService, IOperationService operationService, KafkaProducerService kafkaProducerService) {
this.userManager = userManager;
this.userService = userService;
this.bankAccountService = bankAccountService;
this.operationService = operationService;
this.kafkaProducerService = kafkaProducerService;
}

@Override
public UserResult CreateUser(User user) {
if (user == null || user.getLogin() == null || user.getName() == null) {
return new UserResult.UserCreationError("Некорректные данные пользователя");
try {
if (user == null || user.getLogin() == null || user.getName() == null) {
return new UserResult.UserCreationError("Некорректные данные пользователя");
}
userService.SaveUser(user);
kafkaProducerService.sendEvent("client-topic", String.valueOf(user.getId()), user);
return new UserResult.Success();
} catch (Exception e) {
return new UserResult.UserCreationError(e.getMessage());
}
userService.SaveUser(user);
return new UserResult.Success();
}

@Override
public UserResult UpdateUser(User user) {
try {
userService.SaveUser(user);
kafkaProducerService.sendEvent("client-topic", String.valueOf(user.getId()), user);
return new UserResult.Success();
} catch (Exception e) {
return new UserResult.UserUpdateError(e.getMessage());
Expand Down Expand Up @@ -162,6 +171,7 @@ public BankAccountResult AddBankAccount(int userId, BankAccount bankAccount) {
bankAccount.setId(null);
userManager.AddBankAccount(user, bankAccount);
userService.SaveUser(user);
kafkaProducerService.sendEvent("account-topic", String.valueOf(bankAccount.getId()), bankAccount);
return new BankAccountResult.Success();
} catch (Exception e) {
return new BankAccountResult.BankAccountCreationError(e.getMessage());
Expand All @@ -175,6 +185,7 @@ public BankAccountResult UpdateBankAccount(BankAccount bankAccount) {
return new BankAccountResult.BankAccountUpdateError("Некорректные данные счета");
}
bankAccountService.UpdateAccount(bankAccount);
kafkaProducerService.sendEvent("account-topic", String.valueOf(bankAccount.getId()), bankAccount);
return new BankAccountResult.Success();
} catch (Exception e) {
return new BankAccountResult.BankAccountUpdateError(e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
@RequestMapping(value = "/users", produces = "application/json")
public class UserDTOController {

private final IBaseController baseController;
Expand All @@ -31,7 +31,7 @@ public UserDTOController(IBaseController baseController) {
@ApiResponse(responseCode = "200", description = "Пользователь найден", content = @Content(mediaType = "application/json", schema = @Schema(implementation = UserDTO.class))),
@ApiResponse(responseCode = "404", description = "Пользователь не найден")
})
@GetMapping("/{id}")
@GetMapping(value = "/{id}", produces = "application/json")
public ResponseEntity<UserDTO> getUserById(@Parameter(description = "ID пользователя") @PathVariable int id) {
User user = baseController.GetUserById(id);
if (user == null) {
Expand All @@ -45,7 +45,7 @@ public ResponseEntity<UserDTO> getUserById(@Parameter(description = "ID поль
@ApiResponse(responseCode = "201", description = "Пользователь создан"),
@ApiResponse(responseCode = "400", description = "Некорректные данные")
})
@PostMapping("/create")
@PostMapping(value = "/create", consumes = "application/json", produces = "application/json")
public ResponseEntity<?> createUser(@RequestBody User user) {
UserResult result = baseController.CreateUser(user);
if (result instanceof UserResult.Success) {
Expand All @@ -61,7 +61,7 @@ public ResponseEntity<?> createUser(@RequestBody User user) {
@ApiResponse(responseCode = "404", description = "Пользователь не найден"),
@ApiResponse(responseCode = "400", description = "Некорректные данные")
})
@PutMapping("/update/{id}")
@PutMapping(value = "/update/{id}", consumes = "application/json", produces = "application/json")
public ResponseEntity<?> updateUser(@Parameter(description = "ID пользователя") @PathVariable int id, @RequestBody User user) {
User existingUser = baseController.GetUserById(id);
if (existingUser == null) {
Expand All @@ -80,7 +80,7 @@ public ResponseEntity<?> updateUser(@Parameter(description = "ID пользов
@ApiResponse(responseCode = "204", description = "Пользователь удален"),
@ApiResponse(responseCode = "404", description = "Пользователь не найден")
})
@DeleteMapping("/delete/{id}")
@DeleteMapping(value = "/delete/{id}", produces = "application/json")
public ResponseEntity<?> deleteUser(@Parameter(description = "ID пользователя") @PathVariable int id) {
User user = baseController.GetUserById(id);
if (user == null) {
Expand All @@ -93,4 +93,4 @@ public ResponseEntity<?> deleteUser(@Parameter(description = "ID пользов
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package Presentation.DTO;

import lombok.Data;

@Data
public class CreateBankAccountDTO {
private Integer id;
private double balance;
private Integer userId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package Presentation.Kafka.Configs;

import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.*;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class KafkaProducerConfig {

@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> config = new HashMap<>();
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(config);
}

@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package Presentation.Kafka.Services;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

@Service
public class KafkaProducerService {

private final KafkaTemplate<String, String> kafkaTemplate;
private final ObjectMapper objectMapper = new ObjectMapper();

public KafkaProducerService(KafkaTemplate<String, String> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}

public <T> void sendEvent(String topic, String key, T payload) {
try {
String json = objectMapper.writeValueAsString(payload);
kafkaTemplate.send(topic, key, json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
Loading