diff --git a/pom.xml b/pom.xml
index 93a592ed..c463be91 100644
--- a/pom.xml
+++ b/pom.xml
@@ -231,6 +231,20 @@
spring-test
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+ io.projectreactor.netty
+ reactor-netty
+
+
diff --git a/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java b/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java
index 7ec98464..1465ad7e 100644
--- a/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java
+++ b/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java
@@ -23,7 +23,9 @@ public class TendermanagementApplication {
@Override
public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**").allowedOrigins("http://localhost:3000")
+ //remove after testing
+//add url for a demo html and js project created on Vs code and ran it from go live on right bottim corner user gepafin_dev_local backup DB for gettng notification on FE.
+ registry.addMapping("/**").allowedOrigins("http://127.0.0.1:5500", "http://localhost:3000", "http://localhost:5500")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD").allowCredentials(true);
}
}
diff --git a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java
index 090f3688..8c11eac2 100644
--- a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java
+++ b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java
@@ -109,6 +109,7 @@ public class SecurityConfig {
.requestMatchers("/v1/api-docs/**").permitAll() // API docs
.requestMatchers("/v1/user/reset-password/initiate").permitAll()
.requestMatchers("/v1/user/reset-password").permitAll()
+ .requestMatchers("/wss/**").permitAll() // if this is not running use this /gs-guide-websocket
.anyRequest().authenticated())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
.exceptionHandling(exceptionHandling -> exceptionHandling
diff --git a/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java b/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java
new file mode 100644
index 00000000..b12207e6
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java
@@ -0,0 +1,32 @@
+package net.gepafin.tendermanagement.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.messaging.simp.config.MessageBrokerRegistry;
+import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
+import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
+import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
+
+@Configuration
+@EnableWebSocketMessageBroker
+public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
+
+ @Override
+ public void configureMessageBroker(MessageBrokerRegistry config) {
+ // Enable a simple broker for both /topic (broadcast messages) and /queue (user-specific messages)
+ config.enableStompBrokerRelay("/topic")
+ .setRelayHost("localhost")
+ .setRelayPort(61613) // RabbitMQ is running on port 61613
+ .setClientLogin("guest")
+ .setClientPasscode("guest");
+
+ // Prefix for application messages (user sends messages to /app endpoints)
+ config.setApplicationDestinationPrefixes("/app");
+ }
+
+ @Override
+ public void registerStompEndpoints(StompEndpointRegistry registry) {
+
+ registry.addEndpoint("/gs-guide-websocket").setAllowedOrigins("http://127.0.0.1:5501/", "http://localhost:5500", "http://localhost:5501", "http://127.0.0.1:5500/")
+ .withSockJS();
+ }
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java
index 14d82b91..f0d9a617 100644
--- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java
+++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java
@@ -341,5 +341,9 @@ public class GepafinConstant {
public static final String POLLING_THREAD_NAME = "Ndg-Polling-Thread-";
public static final String DOCUMENT_UPLOADING_IN_PROGRESS = "document.uploading.is.in.progress";
public static final String ASYNC_DOCUMENT_UPLOAD_NAME = "AsyncDocumentUpload-";
+
+ //Notification
+ public static final String COMMON_SINGLE_CHANNEL_PREFIX = "/topic/notifications_user_";
+ public static final String COMPANY_PREFIX = "_company_";
}
diff --git a/src/main/java/net/gepafin/tendermanagement/constants/NotificationConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/NotificationConstant.java
new file mode 100644
index 00000000..3f5c1cdc
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/constants/NotificationConstant.java
@@ -0,0 +1,5 @@
+package net.gepafin.tendermanagement.constants;
+
+public class NotificationConstant {
+ public static final String NOTIFICATION_SENT_SUCCESSFULLY = "Notification Sent Successfully.";
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java b/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java
index 9b3b12a5..772538bb 100644
--- a/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java
+++ b/src/main/java/net/gepafin/tendermanagement/dao/CallDao.java
@@ -15,7 +15,9 @@ import java.util.zip.ZipOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.entities.*;
import net.gepafin.tendermanagement.enums.DocumentSourceTypeEnum;
+import net.gepafin.tendermanagement.enums.NotificationTypeEnum;
import net.gepafin.tendermanagement.enums.VersionActionTypeEnum;
+import net.gepafin.tendermanagement.model.request.NotificationReq;
import net.gepafin.tendermanagement.model.request.VersionHistoryRequest;
import net.gepafin.tendermanagement.model.response.*;
import net.gepafin.tendermanagement.repositories.*;
@@ -108,6 +110,18 @@ public class CallDao {
@Autowired
private HttpServletRequest request;
+ @Autowired
+ private NotificationDao notificationDao;
+
+ @Autowired
+ private BeneficiaryRepository beneficiaryRepository;
+
+ @Autowired
+ private NotificationTypeRepository notificationTypeRepository;
+
+ @Autowired
+ private UserWithCompanyRepository userWithCompanyRepository;
+
public CallResponse createCallStep1(CreateCallRequestStep1 createCallRequest, UserEntity userEntity) {
createCallRequest.setRegionId(userEntity.getRoleEntity().getRegion().getId());
CallEntity callEntity = convertToCallEntity(createCallRequest, userEntity);
@@ -828,13 +842,36 @@ public class CallDao {
validateStatusChange(currentStatus, statusReq);
callEntity.setStatus(statusReq.getValue());
callEntity = callRepository.save(callEntity);
-
+
+ //Creating notification.
+ List userIds = beneficiaryRepository.findUserIdsByHubIdAndBeneficiaryId(callEntity.getHub().getId());
+ Map placeholders = new HashMap<>();
+ placeholders.put("{{call_name}}", callEntity.getName());
+ userIds.forEach(userId -> {
+ NotificationReq notificationReq = createNotificationReq(NotificationTypeEnum.CALL_CREATED.getValue(), placeholders, userId);
+ List companyIds = userWithCompanyRepository.findActiveCompanyIdsByUserId(notificationReq.getUserId());
+ notificationReq.setCompanyIds(companyIds);
+ notificationDao.sendNotification(notificationReq);
+ });
+
/** This code is responsible for adding a version history log for the "update call status" operation **/
loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCallEntity).newData(callEntity).build());
-
+
return convertToCallResponseBean(callEntity);
}
+ public NotificationReq createNotificationReq(String notificationType, Map placeholders, Long userId) {
+ // Create NotificationReq object
+ NotificationReq notificationReq = new NotificationReq();
+ NotificationTypeEntity notificationTypeEntity = notificationTypeRepository.findByNotificationNameAndIsDeletedFalse(notificationType);
+ notificationReq.setNotificationType(notificationType);
+ String message = Utils.replacePlaceholders(notificationTypeEntity.getJsonTemplate(), placeholders);
+ notificationReq.setMessage(message);
+ notificationReq.setUserId(userId);
+ return notificationReq;
+ }
+
+
private void validateStatusChange(CallStatusEnum currentStatus, CallStatusEnum newStatus) {
if (currentStatus == newStatus) {
throw new CustomValidationException(Status.VALIDATION_ERROR,
diff --git a/src/main/java/net/gepafin/tendermanagement/dao/NotificationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/NotificationDao.java
new file mode 100644
index 00000000..17c5a3c8
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/dao/NotificationDao.java
@@ -0,0 +1,97 @@
+package net.gepafin.tendermanagement.dao;
+
+import lombok.extern.slf4j.Slf4j;
+import net.gepafin.tendermanagement.constants.GepafinConstant;
+import net.gepafin.tendermanagement.entities.NotificationEntity;
+import net.gepafin.tendermanagement.enums.NotificationEnum;
+import net.gepafin.tendermanagement.model.request.NotificationReq;
+import net.gepafin.tendermanagement.repositories.NotificationRepository;
+import net.gepafin.tendermanagement.repositories.NotificationTypeRepository;
+import net.gepafin.tendermanagement.util.Utils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+@Slf4j
+public class NotificationDao {
+
+ @Autowired
+ private SimpMessagingTemplate messagingTemplate;
+
+ @Autowired
+ private NotificationRepository notificationRepository;
+
+ @Autowired
+ private NotificationTypeRepository notificationTypeRepository;
+
+ public NotificationReq sendNotification(NotificationReq notificationReq) {
+
+ // Ensure userId is properly set in notificationReq if not already
+ Long userId = notificationReq.getUserId();
+ if (userId == null) {
+ log.error("User ID is missing in the notification request.");
+ return null;
+ }
+ NotificationEntity notificationEntity = saveNotification(notificationReq);
+ log.info("Sending notification to user {} with content: {}", userId, notificationReq.getMessage());
+ List companyIds = notificationReq.getCompanyIds();
+
+ if (companyIds.isEmpty()) {
+ sendToUser(userId, notificationEntity);
+ } else {
+ sendToCompanies(userId, companyIds, notificationEntity);
+ }
+
+ return convertToNotificationReq(notificationEntity);
+ }
+
+ private NotificationEntity saveNotification(NotificationReq notificationReq) {
+
+ NotificationEntity notificationEntity = convertToNotificationEntity(notificationReq);
+ return notificationRepository.save(notificationEntity);
+ }
+
+ private void sendToUser(Long userId, NotificationEntity notificationEntity) {
+
+ String userChannel = GepafinConstant.COMMON_SINGLE_CHANNEL_PREFIX + userId;
+ log.info("Channel for User {}", userChannel);
+ messagingTemplate.convertAndSend(userChannel, notificationEntity);
+ }
+
+ private void sendToCompanies(Long userId, List companyIds, NotificationEntity notificationEntity) {
+ // Send notification to each company provided in the companyIds list
+ companyIds.forEach(companyId -> {
+ String companyChannel = Utils.createChannelForUserAndCompany(userId, companyId);
+ log.info("Channel for User and Company {}, {}", userId, companyChannel);
+ messagingTemplate.convertAndSend(companyChannel, notificationEntity);
+ });
+ }
+
+ private NotificationReq convertToNotificationReq(NotificationEntity notificationEntity) {
+
+ NotificationReq notificationReq = new NotificationReq();
+ notificationReq.setId(notificationEntity.getId());
+ notificationReq.setUserId(notificationEntity.getUserId());
+ notificationReq.setStatus(NotificationEnum.UNREAD.getValue());
+ notificationReq.setMessage(notificationEntity.getMessage());
+ notificationReq.setCreatedDate(notificationEntity.getCreatedDate());
+ notificationReq.setUpdatedDate(notificationEntity.getUpdatedDate());
+ return notificationReq;
+
+ }
+
+ private NotificationEntity convertToNotificationEntity(NotificationReq notificationReq) {
+
+ NotificationEntity notificationEntity = new NotificationEntity();
+ String message = notificationReq.getMessage();
+ notificationEntity.setNotificationType(notificationReq.getNotificationType());
+ notificationEntity.setUserId(notificationReq.getUserId());
+ notificationEntity.setStatus(NotificationEnum.UNREAD.getValue());
+ notificationEntity.setIsDeleted(Boolean.FALSE);
+ notificationEntity.setMessage(message);
+ return notificationEntity;
+ }
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java
new file mode 100644
index 00000000..74131d4c
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java
@@ -0,0 +1,31 @@
+package net.gepafin.tendermanagement.entities;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Entity
+@Table(name = "NOTIFICATION")
+@Data
+public class NotificationEntity extends BaseEntity {
+
+ @Column(name = "USER_ID")
+ Long userId;
+
+ @Column(name = "MESSAGE")
+ String message;
+
+ @Column(name = "STATUS")
+ String status;
+
+ @Column(name = "IS_DELETED")
+ Boolean isDeleted;
+
+ @Column(name = "NOTIFICATION_TYPE")
+ String notificationType;
+
+ @Column(name = "REDIRECT_LINK")
+ String redirectLink;
+
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java
new file mode 100644
index 00000000..5202c97a
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java
@@ -0,0 +1,21 @@
+package net.gepafin.tendermanagement.entities;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Entity
+@Data
+@Table(name = "NOTIFICATION_TYPE")
+public class NotificationTypeEntity extends BaseEntity {
+
+ @Column(name = "NOTIFICATION_NAME")
+ String notificationName;
+
+ @Column(name = "JSON_TEMPLATE")
+ String jsonTemplate;
+
+ @Column(name="IS_DELETED")
+ private Boolean isDeleted;
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/enums/NotificationEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/NotificationEnum.java
new file mode 100644
index 00000000..5b8bf633
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/enums/NotificationEnum.java
@@ -0,0 +1,27 @@
+package net.gepafin.tendermanagement.enums;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum NotificationEnum {
+ //status
+ READ("READ"), UNREAD("UNREAD");
+
+ private final String value;
+
+ NotificationEnum(String value) {
+
+ this.value = value;
+ }
+
+ @JsonValue
+ public String getValue() {
+
+ return value;
+ }
+
+ @Override
+ public String toString() {
+
+ return String.valueOf(value);
+ }
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java
new file mode 100644
index 00000000..86fd75dc
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java
@@ -0,0 +1,29 @@
+package net.gepafin.tendermanagement.enums;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum NotificationTypeEnum {
+ CALL_CREATED("CALL_CREATED"),
+ APPLICATION_SUBMISSION("APPLICATION_SUBMISSION"),
+ AMENDMENT_CREATION("AMENDMENT_CREATION"),
+ EVALUATION_RESULT("EVALUATION_RESULT");
+
+ private final String value;
+
+ NotificationTypeEnum(String value) {
+
+ this.value = value;
+ }
+
+ @JsonValue
+ public String getValue() {
+
+ return value;
+ }
+
+ @Override
+ public String toString() {
+
+ return String.valueOf(value);
+ }
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/NotificationReq.java b/src/main/java/net/gepafin/tendermanagement/model/request/NotificationReq.java
new file mode 100644
index 00000000..8506694a
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/model/request/NotificationReq.java
@@ -0,0 +1,37 @@
+package net.gepafin.tendermanagement.model.request;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class NotificationReq {
+
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ Long id;
+
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ Long userId;
+
+ String message;
+
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ String notificationType;
+
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ String status;
+
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ private LocalDateTime createdDate;
+
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ private LocalDateTime updatedDate;
+
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ String redirectUrl;
+
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ List companyIds;
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/UserResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/UserResponseBean.java
index b0a5ef38..c8724d43 100644
--- a/src/main/java/net/gepafin/tendermanagement/model/response/UserResponseBean.java
+++ b/src/main/java/net/gepafin/tendermanagement/model/response/UserResponseBean.java
@@ -40,6 +40,7 @@ public class UserResponseBean extends BaseBean {
private List companies;
private Boolean privacy;
+
private Boolean terms;
private Boolean marketing;
diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/BeneficiaryRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/BeneficiaryRepository.java
index ecb6ed7d..984e477a 100644
--- a/src/main/java/net/gepafin/tendermanagement/repositories/BeneficiaryRepository.java
+++ b/src/main/java/net/gepafin/tendermanagement/repositories/BeneficiaryRepository.java
@@ -1,11 +1,16 @@
package net.gepafin.tendermanagement.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import net.gepafin.tendermanagement.entities.BeneficiaryEntity;
+import java.util.List;
+
@Repository
public interface BeneficiaryRepository extends JpaRepository {
+ @Query("SELECT u.id FROM UserEntity u JOIN u.beneficiary b WHERE b.id = u.beneficiary.id AND b.hubId = :hubId")
+ List findUserIdsByHubIdAndBeneficiaryId(Long hubId);
}
diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/NotificationRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/NotificationRepository.java
new file mode 100644
index 00000000..a52c39de
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/repositories/NotificationRepository.java
@@ -0,0 +1,7 @@
+package net.gepafin.tendermanagement.repositories;
+
+import net.gepafin.tendermanagement.entities.NotificationEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface NotificationRepository extends JpaRepository {
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/NotificationTypeRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/NotificationTypeRepository.java
new file mode 100644
index 00000000..78cef54d
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/repositories/NotificationTypeRepository.java
@@ -0,0 +1,8 @@
+package net.gepafin.tendermanagement.repositories;
+
+import net.gepafin.tendermanagement.entities.NotificationTypeEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface NotificationTypeRepository extends JpaRepository {
+ NotificationTypeEntity findByNotificationNameAndIsDeletedFalse(String value);
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/service/NotificationService.java b/src/main/java/net/gepafin/tendermanagement/service/NotificationService.java
new file mode 100644
index 00000000..0b6914c2
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/service/NotificationService.java
@@ -0,0 +1,7 @@
+package net.gepafin.tendermanagement.service;
+
+import net.gepafin.tendermanagement.model.request.NotificationReq;
+
+public interface NotificationService {
+ NotificationReq sendNotification(Long userId, NotificationReq notificationReq);
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/NotificationServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/NotificationServiceImpl.java
new file mode 100644
index 00000000..7928f166
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/service/impl/NotificationServiceImpl.java
@@ -0,0 +1,26 @@
+package net.gepafin.tendermanagement.service.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import net.gepafin.tendermanagement.dao.NotificationDao;
+import net.gepafin.tendermanagement.model.request.NotificationReq;
+import net.gepafin.tendermanagement.service.NotificationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+public class NotificationServiceImpl implements NotificationService {
+
+ @Autowired
+ private NotificationDao notificationDao;
+
+ @Override
+ public NotificationReq sendNotification(Long userId, NotificationReq notificationReq) {
+
+ log.info("Sending notification to user {} with content: {}", userId, notificationReq.getMessage());
+ notificationReq.setUserId(userId);
+ notificationReq = notificationDao.sendNotification(notificationReq);
+ return notificationReq;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java
index e93c8ed9..4290bfff 100644
--- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java
+++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java
@@ -669,4 +669,8 @@ public class Utils {
}
return null;
}
+
+ public static String createChannelForUserAndCompany(Long userId, Long companyId) {
+ return GepafinConstant.COMMON_SINGLE_CHANNEL_PREFIX + userId + GepafinConstant.COMPANY_PREFIX + companyId;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/NotificationApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/NotificationApi.java
new file mode 100644
index 00000000..85a4ab27
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/NotificationApi.java
@@ -0,0 +1,30 @@
+package net.gepafin.tendermanagement.web.rest.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.ExampleObject;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import net.gepafin.tendermanagement.model.request.NotificationReq;
+import net.gepafin.tendermanagement.model.response.LookUpDataResponseBean;
+import net.gepafin.tendermanagement.model.util.Response;
+import net.gepafin.tendermanagement.web.rest.api.errors.ErrorConstants;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+public interface NotificationApi {
+ @Operation(summary = "Api to send notification.", responses = { @ApiResponse(responseCode = "200", description = "OK"),
+ @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = @ExampleObject(value =
+ ErrorConstants.NOTFOUND_ERROR_EXAMPLE))),
+ @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = @ExampleObject(value =
+ ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE))),
+ @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = @ExampleObject(value =
+ ErrorConstants.BADREQUEST_ERROR_EXAMPLE))) })
+ @PostMapping(value = "/{userId}", consumes = "application/json", produces = "application/json")
+ ResponseEntity> sendNotification(HttpServletRequest request, @RequestBody NotificationReq notificationReq,
+ @Parameter(description = "The user id", required = true) @PathVariable("userId") Long userId);
+}
diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/NotificationApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/NotificationApiController.java
new file mode 100644
index 00000000..054c4c63
--- /dev/null
+++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/NotificationApiController.java
@@ -0,0 +1,32 @@
+package net.gepafin.tendermanagement.web.rest.api.impl;
+
+import jakarta.servlet.http.HttpServletRequest;
+import net.gepafin.tendermanagement.config.Translator;
+import net.gepafin.tendermanagement.constants.NotificationConstant;
+import net.gepafin.tendermanagement.model.request.NotificationReq;
+import net.gepafin.tendermanagement.model.util.Response;
+import net.gepafin.tendermanagement.service.NotificationService;
+import net.gepafin.tendermanagement.web.rest.api.NotificationApi;
+import net.gepafin.tendermanagement.web.rest.api.errors.Status;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("${openapi.gepafin.base-path:/v1/notification}")
+public class NotificationApiController implements NotificationApi {
+
+ @Autowired
+ private NotificationService notificationService;
+
+ public ResponseEntity> sendNotification(HttpServletRequest request, NotificationReq notificationReq, Long userId) {
+
+ NotificationReq notificationData = notificationService.sendNotification(userId, notificationReq);
+
+ return ResponseEntity.status(HttpStatus.OK)
+ .body(new Response<>(notificationData, Status.SUCCESS, Translator.toLocale(NotificationConstant.NOTIFICATION_SENT_SUCCESSFULLY)));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties
index 37bcabad..11f3d75e 100644
--- a/src/main/resources/application-local.properties
+++ b/src/main/resources/application-local.properties
@@ -1,6 +1,6 @@
# DataSource Configuration
-spring.datasource.url=jdbc:postgresql://localhost:5432/gepafin_local
-spring.datasource.username=postgres
+spring.datasource.url=jdbc:postgresql://localhost:5432/gepafin_dev_local
+spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=org.postgresql.Driver
@@ -20,4 +20,11 @@ appointment.portal.user=UtenzaAPIPortal@621
appointment.portal.password=u13nzaAP1P0rtal
appointment.portal.source=GEPAFINPORTAL
appointment.portal.context=GEPAFINPORTAL
-flagDaFirmare=false
\ No newline at end of file
+flagDaFirmare=false
+
+# RabbitMQ properties for STOMP broker relay
+spring.rabbitmq.host=localhost
+spring.rabbitmq.port=5672
+spring.rabbitmq.username=guest
+spring.rabbitmq.password=guest
+spring.rabbitmq.virtual-host=/
\ No newline at end of file
diff --git a/src/main/resources/db/changelog/db.changelog-1.0.0.xml b/src/main/resources/db/changelog/db.changelog-1.0.0.xml
index eb77049e..fde4441d 100644
--- a/src/main/resources/db/changelog/db.changelog-1.0.0.xml
+++ b/src/main/resources/db/changelog/db.changelog-1.0.0.xml
@@ -1992,4 +1992,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/db/dump/insert_json_template_for_notification_13_12_2024.sql b/src/main/resources/db/dump/insert_json_template_for_notification_13_12_2024.sql
new file mode 100644
index 00000000..50caaefa
--- /dev/null
+++ b/src/main/resources/db/dump/insert_json_template_for_notification_13_12_2024.sql
@@ -0,0 +1,5 @@
+INSERT INTO notification_type (notification_name, json_template) VALUES
+('CALL_CREATED', 'Un nuovo bando intitolato {{call_name}} è stato pubblicato. Controllalo e invia le tue candidature prima della scadenza.'),
+('APPLICATION_SUBMISSION', 'La tua richiesta per {{call_name}} ai sensi del protocollo n. {{protocol_number}} è stata presentata con successo. È ora in fase di valutazione.'),
+('AMENDMENT_CREATION', 'È stato creato un emendamento per la tua domanda in {{call_name}} ai sensi del protocollo n. {{protocol_number}}. Esamina le modifiche e procedi di conseguenza.'),
+('EVALUATION_RESULT', 'Il risultato della valutazione per la tua domanda ai sensi del protocollo n. {{protocol_number}} è ora disponibile. Fai clic qui per visualizzare il tuo feedback.');