mirror of
https://github.com/ulitsaRaskolnikova/is-coursework.git
synced 2026-09-14 14:05:36 +05:00
feat(domain-and-notification): add notifications for domain activating and renewing
This commit is contained in:
parent
36df912744
commit
6c71bfc0fa
@ -6,5 +6,6 @@ public enum NotificationType {
|
||||
DOMAIN_ACTIVATED,
|
||||
DOMAIN_EXPIRING_SOON,
|
||||
DOMAIN_EXPIRED,
|
||||
DOMAIN_RENEWED,
|
||||
EMAIL_VERIFICATION
|
||||
}
|
||||
|
||||
@ -0,0 +1,105 @@
|
||||
package ru.itmo.domain.client;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import ru.itmo.common.notification.NotificationType;
|
||||
import ru.itmo.common.notification.SendNotificationRequest;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Клиент для отправки уведомлений через notification-service.
|
||||
* Получает JWT-токен из текущего HTTP-запроса и пробрасывает его.
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NotificationClient {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
private final HttpServletRequest httpServletRequest;
|
||||
private final String notificationServiceUrl;
|
||||
|
||||
public NotificationClient(RestTemplate restTemplate,
|
||||
HttpServletRequest httpServletRequest,
|
||||
@Value("${services.notification.url}") String notificationServiceUrl) {
|
||||
this.restTemplate = restTemplate;
|
||||
this.httpServletRequest = httpServletRequest;
|
||||
this.notificationServiceUrl = notificationServiceUrl;
|
||||
}
|
||||
|
||||
public void sendDomainsActivated(UUID userId, List<String> domains, String expiresAt) {
|
||||
try {
|
||||
SendNotificationRequest request = new SendNotificationRequest();
|
||||
request.setUserId(userId);
|
||||
request.setType(NotificationType.DOMAIN_ACTIVATED);
|
||||
request.setSubject("Активировано доменов: " + domains.size());
|
||||
request.setParameters(Map.of(
|
||||
"domains", domains,
|
||||
"expiresAt", expiresAt
|
||||
));
|
||||
|
||||
String jwtToken = extractJwtToken();
|
||||
if (jwtToken == null) {
|
||||
log.warn("No JWT token found, skipping notification for domains: {}", domains);
|
||||
return;
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(jwtToken);
|
||||
|
||||
HttpEntity<SendNotificationRequest> entity = new HttpEntity<>(request, headers);
|
||||
|
||||
String url = notificationServiceUrl + "/notifications/send";
|
||||
restTemplate.exchange(url, HttpMethod.POST, entity, Void.class);
|
||||
log.info("Domain activation notification sent for {} domains", domains.size());
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to send domain activation notification: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void sendDomainsRenewed(UUID userId, Map<String, String> domainsWithExpiry) {
|
||||
try {
|
||||
SendNotificationRequest request = new SendNotificationRequest();
|
||||
request.setUserId(userId);
|
||||
request.setType(NotificationType.DOMAIN_RENEWED);
|
||||
request.setSubject("Продлено доменов: " + domainsWithExpiry.size());
|
||||
request.setParameters(Map.of("domains", domainsWithExpiry));
|
||||
|
||||
String jwtToken = extractJwtToken();
|
||||
if (jwtToken == null) {
|
||||
log.warn("No JWT token found, skipping renewal notification");
|
||||
return;
|
||||
}
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setBearerAuth(jwtToken);
|
||||
|
||||
HttpEntity<SendNotificationRequest> entity = new HttpEntity<>(request, headers);
|
||||
|
||||
String url = notificationServiceUrl + "/notifications/send";
|
||||
restTemplate.exchange(url, HttpMethod.POST, entity, Void.class);
|
||||
log.info("Domain renewal notification sent for {} domains", domainsWithExpiry.size());
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to send domain renewal notification: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String extractJwtToken() {
|
||||
String authHeader = httpServletRequest.getHeader("Authorization");
|
||||
if (authHeader != null && authHeader.startsWith("Bearer ")) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ package ru.itmo.domain.service.impl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.itmo.common.audit.AuditClient;
|
||||
import ru.itmo.domain.client.NotificationClient;
|
||||
import ru.itmo.domain.entity.Domain;
|
||||
import ru.itmo.domain.exception.ForbiddenException;
|
||||
import ru.itmo.domain.exception.L2DomainNotFoundException;
|
||||
@ -14,6 +15,7 @@ import ru.itmo.domain.service.UserDomainService;
|
||||
import ru.itmo.domain.util.SecurityUtil;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@ -21,12 +23,16 @@ import java.util.UUID;
|
||||
@Service
|
||||
public class UserDomainServiceImpl implements UserDomainService {
|
||||
|
||||
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
|
||||
|
||||
private final DomainRepository domainRepository;
|
||||
private final AuditClient auditClient;
|
||||
private final NotificationClient notificationClient;
|
||||
|
||||
public UserDomainServiceImpl(DomainRepository domainRepository, AuditClient auditClient) {
|
||||
public UserDomainServiceImpl(DomainRepository domainRepository, AuditClient auditClient, NotificationClient notificationClient) {
|
||||
this.domainRepository = domainRepository;
|
||||
this.auditClient = auditClient;
|
||||
this.notificationClient = notificationClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -106,6 +112,12 @@ public class UserDomainServiceImpl implements UserDomainService {
|
||||
}
|
||||
|
||||
auditClient.log("Created " + createdDomains.size() + " domains (period=" + period + "): " + String.join(", ", createdDomains), userId);
|
||||
|
||||
// Отправляем одно email-уведомление со всеми созданными доменами
|
||||
if (!createdDomains.isEmpty()) {
|
||||
notificationClient.sendDomainsActivated(userId, createdDomains, finishedAt.format(DATE_FMT));
|
||||
}
|
||||
|
||||
return createdDomains;
|
||||
}
|
||||
|
||||
@ -119,6 +131,7 @@ public class UserDomainServiceImpl implements UserDomainService {
|
||||
|
||||
DomainPeriod period = request.getPeriod();
|
||||
List<String> renewedDomains = new ArrayList<>();
|
||||
java.util.LinkedHashMap<String, String> domainsWithExpiry = new java.util.LinkedHashMap<>();
|
||||
|
||||
for (String l3Domain : request.getL3Domains()) {
|
||||
String l3Name = l3Domain == null ? null : l3Domain.trim();
|
||||
@ -152,13 +165,20 @@ public class UserDomainServiceImpl implements UserDomainService {
|
||||
if (baseDate == null || baseDate.isBefore(LocalDateTime.now())) {
|
||||
baseDate = LocalDateTime.now();
|
||||
}
|
||||
l3.setFinishedAt(calculateFinishedAt(baseDate, period));
|
||||
LocalDateTime newFinishedAt = calculateFinishedAt(baseDate, period);
|
||||
l3.setFinishedAt(newFinishedAt);
|
||||
domainRepository.save(l3);
|
||||
|
||||
renewedDomains.add(l3Name);
|
||||
domainsWithExpiry.put(l3Name, newFinishedAt.format(DATE_FMT));
|
||||
}
|
||||
|
||||
auditClient.log("Renewed " + renewedDomains.size() + " domains (period=" + period + "): " + String.join(", ", renewedDomains), userId);
|
||||
|
||||
if (!domainsWithExpiry.isEmpty()) {
|
||||
notificationClient.sendDomainsRenewed(userId, domainsWithExpiry);
|
||||
}
|
||||
|
||||
return renewedDomains;
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,10 @@ exdns:
|
||||
base-url: http://exdns:8000
|
||||
api-token: ${EXDNS_API_TOKEN:-changeme}
|
||||
|
||||
services:
|
||||
notification:
|
||||
url: http://notification-service:8085
|
||||
|
||||
audit:
|
||||
client:
|
||||
base-url: http://audit-service:8087
|
||||
|
||||
@ -47,6 +47,10 @@ exdns:
|
||||
jwt:
|
||||
secret: ${JWT_SECRET:your-256-bit-secret-key-must-be-at-least-32-characters-long-for-security}
|
||||
|
||||
services:
|
||||
notification:
|
||||
url: ${NOTIFICATION_SERVICE_URL:http://localhost:8085}
|
||||
|
||||
audit:
|
||||
client:
|
||||
base-url: ${AUDIT_SERVICE_URL:http://localhost:8087}
|
||||
|
||||
@ -21,6 +21,7 @@ public class EmailTemplateService {
|
||||
case DOMAIN_ACTIVATED -> "domain-activated";
|
||||
case DOMAIN_EXPIRING_SOON -> "domain-expiring-soon";
|
||||
case DOMAIN_EXPIRED -> "domain-expired";
|
||||
case DOMAIN_RENEWED -> "domain-renewed";
|
||||
case EMAIL_VERIFICATION -> "email-verification";
|
||||
};
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ public class NotificationService {
|
||||
case DOMAIN_ACTIVATED -> "Домен активирован";
|
||||
case DOMAIN_EXPIRING_SOON -> "Напоминание: срок действия домена истекает";
|
||||
case DOMAIN_EXPIRED -> "Срок действия домена истек";
|
||||
case DOMAIN_RENEWED -> "Домены продлены";
|
||||
case EMAIL_VERIFICATION -> "Подтверждение email адреса";
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,15 +2,29 @@
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Домен активирован</title>
|
||||
<title>Домены активированы</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Домен успешно активирован</h2>
|
||||
<p>Здравствуйте!</p>
|
||||
<p>Ваш домен был успешно активирован и готов к использованию.</p>
|
||||
<p th:if="${domainName}">Домен: <strong th:text="${domainName}"></strong></p>
|
||||
<p th:if="${expiresAt}">Срок действия до: <strong th:text="${expiresAt}"></strong></p>
|
||||
<p>Теперь вы можете управлять DNS-записями домена в личном кабинете.</p>
|
||||
<p>С уважением,<br>Команда Domain Registrar</p>
|
||||
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||||
<h2>Домены успешно активированы</h2>
|
||||
<p>Здравствуйте!</p>
|
||||
<p>Ваши домены были успешно активированы и готовы к использованию.</p>
|
||||
<table th:if="${domains}" style="border-collapse: collapse; width: 100%; margin: 16px 0;">
|
||||
<thead>
|
||||
<tr style="background-color: #f2f2f2;">
|
||||
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Домен</th>
|
||||
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Срок действия до</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="d : ${domains}">
|
||||
<td style="border: 1px solid #ddd; padding: 8px;" th:text="${d}"></td>
|
||||
<td style="border: 1px solid #ddd; padding: 8px;" th:text="${expiresAt}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Теперь вы можете управлять DNS-записями доменов в личном кабинете.</p>
|
||||
<p>С уважением,<br>Команда Domain Registrar</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Домены продлены</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||||
<h2>Домены успешно продлены</h2>
|
||||
<p>Здравствуйте!</p>
|
||||
<p>Срок действия ваших доменов был успешно продлён.</p>
|
||||
<table th:if="${domains}" style="border-collapse: collapse; width: 100%; margin: 16px 0;">
|
||||
<thead>
|
||||
<tr style="background-color: #f2f2f2;">
|
||||
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Домен</th>
|
||||
<th style="border: 1px solid #ddd; padding: 8px; text-align: left;">Новый срок действия до</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="entry : ${domains}">
|
||||
<td style="border: 1px solid #ddd; padding: 8px;" th:text="${entry.key}"></td>
|
||||
<td style="border: 1px solid #ddd; padding: 8px;" th:text="${entry.value}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Вы можете управлять DNS-записями доменов в личном кабинете.</p>
|
||||
<p>С уважением,<br>Команда Domain Registrar</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user