Как расширяли функционал последнего проекта
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Расширение функционала последнего проекта
В моем последнем проекте я разрабатывал микросервисное приложение для обработки заказов (Order Management System). Расширение функционала происходило итеративно, с учетом требований клиента и технических ограничений.
Начальное состояние проекта
Проект был построен на базовой архитектуре:
- Spring Boot 3.1 с REST API
- PostgreSQL для хранения заказов
- Простая CRUD логика без кэширования
- Синхронная обработка платежей
- Без разделения ролей пользователей
Анализ требований расширения
Клиент попросил:
- Поддержка различных статусов заказов с отслеживанием истории изменений
- Интеграция с платежной системой Stripe для обработки платежей
- Асинхронная отправка уведомлений (email, SMS) при изменении статуса
- Кэширование данных для оптимизации производительности
- Разделение ролей (администраторы, менеджеры, клиенты)
- Аналитика — отчёты по продажам и популярным товарам
Этап 1: Управление статусами заказов
Добавил Order Status History — аудит всех изменений:
@Entity
@Table(name = "orders")
public class Order {
@Id
private UUID id;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order")
private List<OrderStatusHistory> statusHistory = new ArrayList<>();
private BigDecimal totalAmount;
private LocalDateTime createdAt;
}
@Entity
@Table(name = "order_status_history")
public class OrderStatusHistory {
@Id
private UUID id;
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
@Enumerated(EnumType.STRING)
private OrderStatus previousStatus;
@Enumerated(EnumType.STRING)
private OrderStatus newStatus;
private String reason; // Причина изменения
private LocalDateTime changedAt;
private String changedBy; // Кто изменил
}
public enum OrderStatus {
PENDING, // Ожидание подтверждения
CONFIRMED, // Подтвержден
PROCESSING, // На обработке
SHIPPED, // Отправлен
DELIVERED, // Доставлен
CANCELLED, // Отмен
REFUNDED // Возврат
}
Этап 2: Интеграция со Stripe
Добавил Payment Service для безопасной обработки платежей:
@Service
public class PaymentService {
private final StripeService stripeService;
private final OrderRepository orderRepository;
private final EventPublisher eventPublisher;
@Transactional
public PaymentResult processPayment(UUID orderId, String paymentMethodId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
try {
PaymentIntentRequest request = new PaymentIntentRequest()
.setAmount(order.getTotalAmount().longValue() * 100) // Stripe использует центы
.setCurrency("usd")
.setPaymentMethod(paymentMethodId)
.setConfirm(true);
PaymentIntent intent = stripeService.createPaymentIntent(request);
if ("succeeded".equals(intent.getStatus())) {
order.setStatus(OrderStatus.CONFIRMED);
order.setPaymentId(intent.getId());
orderRepository.save(order);
eventPublisher.publish(new OrderPaymentSuccessEvent(orderId));
return new PaymentResult(true, "Payment successful");
} else if ("requires_action".equals(intent.getStatus())) {
return new PaymentResult(false, "3D Secure required");
}
} catch (StripeException e) {
eventPublisher.publish(new OrderPaymentFailedEvent(orderId, e.getMessage()));
throw new PaymentProcessingException("Payment failed", e);
}
return new PaymentResult(false, "Payment declined");
}
}
Этап 3: Асинхронная обработка через Kafka
Добавил Event-driven архитектуру для отправки уведомлений:
@Service
public class NotificationService {
@KafkaListener(topics = "order-events", groupId = "notification-group")
public void handleOrderEvent(OrderEvent event) {
switch(event.getEventType()) {
case PAYMENT_SUCCESS:
sendConfirmationEmail(event.getOrderId());
break;
case ORDER_SHIPPED:
sendShipmentNotification(event.getOrderId());
break;
case ORDER_DELIVERED:
sendDeliveryConfirmation(event.getOrderId());
break;
}
}
@Async
private void sendConfirmationEmail(UUID orderId) {
Order order = orderRepository.findById(orderId).orElse(null);
if (order != null) {
emailService.send(
order.getCustomerEmail(),
"Order Confirmed",
generateConfirmationTemplate(order)
);
}
}
}
@Configuration
public class KafkaProducerConfig {
@Bean
public KafkaTemplate<String, OrderEvent> kafkaTemplate(ProducerFactory<String, OrderEvent> producerFactory) {
return new KafkaTemplate<>(producerFactory);
}
}
Этап 4: Кэширование с Redis
Добавил многоуровневое кэширование для оптимизации:
@Service
@CacheConfig(cacheNames = "orders")
public class OrderService {
private final OrderRepository orderRepository;
private final CacheManager cacheManager;
@Cacheable(value = "orders", key = "#id")
public Order getOrderById(UUID id) {
return orderRepository.findById(id)
.orElseThrow(() -> new OrderNotFoundException(id));
}
@CacheEvict(value = "orders", key = "#id")
@Transactional
public Order updateOrder(UUID id, UpdateOrderRequest request) {
Order order = getOrderById(id);
order.setStatus(request.getStatus());
return orderRepository.save(order);
}
@Cacheable(value = "customer-orders", key = "#customerId")
public List<Order> getCustomerOrders(UUID customerId) {
return orderRepository.findByCustomerId(customerId);
}
}
@Configuration
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()
)
);
return RedisCacheManager.create(connectionFactory);
}
}
Этап 5: Управление доступом (RBAC)
Добавил Role-Based Access Control с Spring Security:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/orders/**").hasAnyRole("USER", "ADMIN")
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic();
return http.build();
}
}
@Service
public class OrderService {
@PreAuthorize("#customerId == authentication.principal.id or hasRole('ADMIN')")
public Order getOrder(UUID orderId, UUID customerId) {
return orderRepository.findByIdAndCustomerId(orderId, customerId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
}
@PreAuthorize("hasRole('ADMIN')")
@Transactional
public void cancelOrder(UUID orderId, String reason) {
Order order = orderRepository.findById(orderId)
.orElseThrow();
order.setStatus(OrderStatus.CANCELLED);
orderRepository.save(order);
}
}
Этап 6: Аналитика и отчеты
Добавил Analytics Service для бизнес-инсайтов:
@Service
public class AnalyticsService {
@Query("""
SELECT new com.orders.dto.SalesReport(
CAST(o.createdAt AS DATE),
COUNT(o),
SUM(o.totalAmount)
)
FROM Order o
WHERE o.createdAt >= :from AND o.createdAt <= :to
GROUP BY CAST(o.createdAt AS DATE)
ORDER BY CAST(o.createdAt AS DATE) DESC
""")
List<SalesReport> getDailySalesReport(
@Param("from") LocalDateTime from,
@Param("to") LocalDateTime to
);
public List<ProductAnalytics> getTopProducts(int limit) {
return orderRepository.findTopProducts(limit);
}
}
Интеграция компонентов
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
private final PaymentService paymentService;
private final KafkaTemplate<String, OrderEvent> kafkaTemplate;
@PostMapping("/{id}/confirm")
public ResponseEntity<OrderDto> confirmOrder(
@PathVariable UUID id,
@RequestBody PaymentRequest paymentRequest) {
// Обработка платежа
PaymentResult result = paymentService.processPayment(
id,
paymentRequest.getPaymentMethodId()
);
// Публикация события в Kafka
kafkaTemplate.send("order-events",
new OrderEvent(id, EventType.PAYMENT_SUCCESS));
// Получение заказа (кэшированный)
Order order = orderService.getOrderById(id);
return ResponseEntity.ok(OrderDto.from(order));
}
}
Результаты расширения
После всех изменений система:
- Обрабатывает 10x больше заказов благодаря асинхронности
- Уменьшила latency на 60% через кэширование
- Улучшила безопасность с помощью RBAC и Stripe интеграции
- Предоставляет аналитику для бизнес-решений
- Остается масштабируемой с event-driven архитектурой
Каждый этап был протестирован, документирован, и развернут в production с мониторингом метрик.