From 80577c23f8d8ce9ba870ab26669835aa83ab5a65 Mon Sep 17 00:00:00 2001 From: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Date: Thu, 19 Jun 2025 19:53:00 +0530 Subject: [PATCH 1/5] Add Video Call Consultation Service (#180) * Add the functionality for video call consultation service * Add the necessary imports for the application * Update video call service with DB changes * Add update api for call status updation * Add service class to prepare the sms template and include benregid * update the properties in common_example file * Fix the issue in date time conversion * Add the link generated time and islinkused flag for the meeting link * Change the consultation date format and response format for send link api * Add the video call url properties in environment files * Add recording functionality * Remove dev and test properties as it not in develop * feat:fix the alignment * feat:remove cross origins and add variable name for ci file * Add the properties in docker file * Add recording properties in properties file * fix:coderabbit comments --- src/main/environment/common_ci.properties | 5 +- src/main/environment/common_docker.properties | 6 +- .../environment/common_example.properties | 12 +- .../videocall/VideoCallController.java | 99 ++++++++++++ .../common/data/sms/SMSParametersMap.java | 14 +- .../com/iemr/common/data/sms/SMSTemplate.java | 10 +- .../data/videocall/VideoCallParameters.java | 80 ++++++++++ .../com/iemr/common/mapper/sms/SMSMapper.java | 2 +- .../mapper/videocall/VideoCallMapper.java | 37 +++++ .../com/iemr/common/model/sms/SMSRequest.java | 4 +- .../model/videocall/UpdateCallRequest.java | 12 ++ .../model/videocall/UpdateCallResponse.java | 21 +++ .../model/videocall/VideoCallRequest.java | 27 ++++ .../VideoCallParameterRepository.java | 32 ++++ .../common/service/sms/SMSServiceImpl.java | 145 ++++++++++++++++-- .../service/videocall/VideoCallService.java | 13 ++ .../videocall/VideoCallServiceImpl.java | 130 ++++++++++++++++ src/main/resources/application.properties | 8 +- 18 files changed, 624 insertions(+), 33 deletions(-) create mode 100644 src/main/java/com/iemr/common/controller/videocall/VideoCallController.java create mode 100644 src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java create mode 100644 src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java create mode 100644 src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java create mode 100644 src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java create mode 100644 src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java create mode 100644 src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java create mode 100644 src/main/java/com/iemr/common/service/videocall/VideoCallService.java create mode 100644 src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 2defbace..1bcf2b13 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -179,5 +179,6 @@ captcha.enable-captcha=@env.ENABLE_CAPTCHA@ cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@ - - +video-call-url=@env.VIDEO_CALL_URL@ +jibri.output.path=@env.JIBRI_OUTPUT_PATH@ +video.recording.path=@env.VIDEO_RECORDING_PATH@ \ No newline at end of file diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 56e2a2fe..6799c959 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -178,4 +178,8 @@ captcha.secret-key=${CAPTCHA_SECRET_KEY} captcha.verify-url=${CAPTCHA_VERIFY_URL} captcha.enable-captcha=${ENABLE_CAPTCHA} -cors.allowed-origins=${CORS_ALLOWED_ORIGINS} \ No newline at end of file +cors.allowed-origins=${CORS_ALLOWED_ORIGINS} + +video-call-url=${VIDEO_CALL_URL} +jibri.output.path={JIBRI_OUTPUT_PATH} +video.recording.path={VIDEO_RECORDING_PATH} \ No newline at end of file diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index 76144058..7bc96104 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -30,15 +30,15 @@ cti-server-ip=10.208.122.99 cti-logger_base_url=http://10.208.122.99/logger # Identity Config -identity-api-url = http://localhost:8094/ +identity-api-url = http://localhost:8094 #Verify whether 1097 and identity are same? -identity-1097-api-url = http://localhost:8095/ +identity-1097-api-url = http://localhost:8095 ##Generate Benificiary Config -genben-api=http://localhost:8092/ +genben-api=http://localhost:8092 #### SMS Configuration send-sms=false -sendSMSUrl = http://localhost:8080/sms/sendSMS +sendSMSUrl = http://localhost:8083/sms/sendSMS source-address=AIDSHL sms-username= sms-password= @@ -198,6 +198,10 @@ grievanceAllocationRetryConfiguration=3 logging.path=logs/ logging.file.name=logs/common-api.log +video-call-url=https://vc.piramalswasthya.org/ +jibri.output.path=/srv/jibri/recordings +video.recording.path=/srv/recordings + captcha.secret-key= captcha.verify-url= https://challenges.cloudflare.com/turnstile/v0/siteverify captcha.enable-captcha=true diff --git a/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java b/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java new file mode 100644 index 00000000..2b0c48d4 --- /dev/null +++ b/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java @@ -0,0 +1,99 @@ +package com.iemr.common.controller.videocall; + +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.iemr.common.model.videocall.UpdateCallRequest; +import com.iemr.common.model.videocall.VideoCallRequest; +import com.iemr.common.service.videocall.VideoCallService; +import com.iemr.common.utils.response.OutputResponse; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; + +import org.springframework.web.bind.annotation.RequestBody; + +@RestController +@RequestMapping(value = "/video-consultation") +public class VideoCallController { + final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + @Autowired + private VideoCallService videoCallService; + + @CrossOrigin() + @PostMapping(value = "/generate-link", produces = MediaType.APPLICATION_JSON_VALUE, headers = "Authorization") + public ResponseEntity> generateJitsiLink() { + Map response = new HashMap<>(); + try { + String link = videoCallService.generateMeetingLink(); + response.put("meetingLink", link); + return ResponseEntity.ok(response); + } catch (Exception e) { + response.put("error", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + +@PostMapping(value = "/send-link", produces = MediaType.APPLICATION_JSON_VALUE, headers = "Authorization") +public String sendVideoLink(@RequestBody String requestModel, HttpServletRequest request) { + OutputResponse response = new OutputResponse(); + + try { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + VideoCallRequest requestData = objectMapper.readValue(requestModel, VideoCallRequest.class); + String serviceResponse = videoCallService.sendMeetingLink(requestData); + + return serviceResponse; + + } catch (Exception e) { + logger.error("send MeetingLink failed with error: " + e.getMessage(), e); + response.setError(e); + return response.toString(); + } +} + +@PostMapping(value = "/update-call-status", produces = MediaType.APPLICATION_JSON_VALUE, headers = "Authorization") +public ResponseEntity updateCallStatus(@RequestBody UpdateCallRequest requestModel, HttpServletRequest request) { + OutputResponse response = new OutputResponse(); + + try { + if (requestModel.getMeetingLink() == null || requestModel.getCallStatus() == null) { + throw new IllegalArgumentException("Meeting Link and Status are required"); + } + + String result = videoCallService.updateCallStatus(requestModel); + + JSONObject responseObj = new JSONObject(); + responseObj.put("status", "success"); + responseObj.put("message", result); + response.setResponse(responseObj.toString()); + + } catch (IllegalArgumentException e) { + logger.error("Validation error: " + e.getMessage(), e); + return ResponseEntity.badRequest().body("{\"status\":\"error\",\"message\":\"" + e.getMessage() + "\"}"); + } catch (Exception e) { + logger.error("updateCallStatus failed with error: " + e.getMessage(), e); + response.setError(e); + } + + return ResponseEntity.ok(response.toString()); +} + + + +} diff --git a/src/main/java/com/iemr/common/data/sms/SMSParametersMap.java b/src/main/java/com/iemr/common/data/sms/SMSParametersMap.java index 4b07bb1a..5593aed7 100644 --- a/src/main/java/com/iemr/common/data/sms/SMSParametersMap.java +++ b/src/main/java/com/iemr/common/data/sms/SMSParametersMap.java @@ -24,7 +24,9 @@ import java.sql.Timestamp; import com.iemr.common.utils.mapper.OutputMapper; - +import com.google.gson.GsonBuilder; +import com.google.gson.LongSerializationPolicy; +import com.google.gson.annotations.Expose; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -69,9 +71,11 @@ public class SMSParametersMap @Column(name = "LastModDate", insertable = false, updatable = false) Timestamp lastModDate; - @Override - public String toString() - { - return OutputMapper.gsonWithoutExposeRestriction().toJson(this); +@Override + public String toString() { + + return new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setLongSerializationPolicy(LongSerializationPolicy.STRING).serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").create().toJson(this); } + } diff --git a/src/main/java/com/iemr/common/data/sms/SMSTemplate.java b/src/main/java/com/iemr/common/data/sms/SMSTemplate.java index 95f465d0..d0467b30 100644 --- a/src/main/java/com/iemr/common/data/sms/SMSTemplate.java +++ b/src/main/java/com/iemr/common/data/sms/SMSTemplate.java @@ -26,6 +26,8 @@ import com.iemr.common.utils.mapper.OutputMapper; +import com.google.gson.GsonBuilder; +import com.google.gson.LongSerializationPolicy; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -77,8 +79,10 @@ public class SMSTemplate Timestamp lastModDate; @Override - public String toString() - { - return OutputMapper.gsonWithoutExposeRestriction().toJson(this); + public String toString() { + + return new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setLongSerializationPolicy(LongSerializationPolicy.STRING).serializeNulls() + .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").create().toJson(this); } + } diff --git a/src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java b/src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java new file mode 100644 index 00000000..c9df2d87 --- /dev/null +++ b/src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java @@ -0,0 +1,80 @@ +package com.iemr.common.data.videocall; + +import java.sql.Timestamp; + +import com.iemr.common.utils.mapper.OutputMapper; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +@Entity +@Table(name = "t_videocallparameter") +@Data +public class VideoCallParameters { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "MeetingID") + private Integer meetingID; + + @Column(name = "DateOfCall") + private Timestamp dateOfCall; + + @Column(name = "CallerPhoneNumber") + private String callerPhoneNumber; + + @Column(name = "AgentID") + private String agentID; + + @Column(name = "AgentName") + private String agentName; + + @Column(name = "MeetingLink") + private String meetingLink; + + @Column(name = "CallStatus") + private String callStatus; + + @Column(name = "CallDuration") + private String callDuration; + + @Column(name = "ProviderServiceMapID") + private Integer providerServiceMapID; + + @Column(name = "BeneficiaryRegID") + private Long beneficiaryRegID; + + @Column(name = "ClosureRemark") + private String closureRemark; + + @Column(name = "LinkGeneratedAt") + private Timestamp linkGeneratedAt; + + @Column(name = "IsLinkUsed") + private boolean linkUsed; + + @Column(name = "Deleted", insertable = false, updatable = true) + private Boolean deleted; + + @Column(name = "CreatedBy", insertable = true, updatable = false) + private String createdBy; + + @Column(name = "CreatedDate", insertable = false, updatable = false) + private Timestamp createdDate; + + @Column(name = "ModifiedBy", insertable = false, updatable = true) + private String modifiedBy; + + @Column(name = "LastModDate", insertable = false, updatable = false) + private Timestamp lastModDate; + + @Override + public String toString() + { + return OutputMapper.gsonWithoutExposeRestriction().toJson(this); + } +} diff --git a/src/main/java/com/iemr/common/mapper/sms/SMSMapper.java b/src/main/java/com/iemr/common/mapper/sms/SMSMapper.java index 6dce2624..7fe629f1 100644 --- a/src/main/java/com/iemr/common/mapper/sms/SMSMapper.java +++ b/src/main/java/com/iemr/common/mapper/sms/SMSMapper.java @@ -48,7 +48,7 @@ public interface SMSMapper SMSMapper INSTANCE = Mappers.getMapper(SMSMapper.class); - @Mappings({ @Mapping(source = "request.smsTemplateTypeID", target = "smsTypeID"), }) + @Mappings({ @Mapping(source = "request.smsTemplateTypeID", target = "smsTypeID") }) SMSTemplate requestToSMSTemplate(SMSRequest request); @IterableMapping(elementTargetType = SMSTemplate.class) diff --git a/src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java b/src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java new file mode 100644 index 00000000..521d5921 --- /dev/null +++ b/src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java @@ -0,0 +1,37 @@ +package com.iemr.common.mapper.videocall; + +import java.util.List; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import org.mapstruct.IterableMapping; +import org.mapstruct.factory.Mappers; + +import com.iemr.common.data.videocall.VideoCallParameters; +import com.iemr.common.model.videocall.UpdateCallRequest; +import com.iemr.common.model.videocall.UpdateCallResponse; +import com.iemr.common.model.videocall.VideoCallRequest; + +@Mapper(componentModel = "spring") +public interface VideoCallMapper { + VideoCallMapper INSTANCE = Mappers.getMapper(VideoCallMapper.class); + + VideoCallRequest videoCallToRequest(VideoCallParameters videoCall); + + VideoCallParameters videoCallToEntity(VideoCallRequest videoCallRequest); + + @IterableMapping(elementTargetType = VideoCallRequest.class) + List videoCallToRequestList(List videoCallList); + + @IterableMapping(elementTargetType = VideoCallParameters.class) + List videoCallToEntityList(List videoCallRequestList); + + VideoCallParameters updateRequestToVideoCall(UpdateCallRequest updateCallStatusRequest); + + UpdateCallResponse videoCallToResponse(VideoCallParameters videoCall); + + @IterableMapping(elementTargetType = VideoCallParameters.class) + List updateRequestToVideoCall(List updateCallStatusRequests); + + @IterableMapping(elementTargetType = UpdateCallResponse.class) + List videoCallToResponse(List videoCalls); +} diff --git a/src/main/java/com/iemr/common/model/sms/SMSRequest.java b/src/main/java/com/iemr/common/model/sms/SMSRequest.java index f3494a53..41fdc6c9 100644 --- a/src/main/java/com/iemr/common/model/sms/SMSRequest.java +++ b/src/main/java/com/iemr/common/model/sms/SMSRequest.java @@ -23,7 +23,7 @@ import java.sql.Timestamp; import java.util.List; - +import com.fasterxml.jackson.annotation.JsonProperty; import com.iemr.common.data.telemedicine.PrescribedDrugDetail; import lombok.Data; @@ -78,4 +78,6 @@ public class SMSRequest { private Long beneficiaryId; private String appointmentDate; private String appointmentTime; + @JsonProperty("sms_Advice") + private String smsAdvice; } diff --git a/src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java b/src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java new file mode 100644 index 00000000..343198b3 --- /dev/null +++ b/src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java @@ -0,0 +1,12 @@ +package com.iemr.common.model.videocall; + +import lombok.Data; + +@Data +public class UpdateCallRequest { + + private String meetingLink; + private String callStatus; + private String callDuration; + private String modifiedBy; +} diff --git a/src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java b/src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java new file mode 100644 index 00000000..f01f46f5 --- /dev/null +++ b/src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java @@ -0,0 +1,21 @@ +package com.iemr.common.model.videocall; + +import java.sql.Timestamp; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import lombok.Data; + +@Data +public class UpdateCallResponse { + private String meetingLink; + private String callStatus; + private String callDuration; + private String modifiedBy; + private boolean isLinkUsed; + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") + private Timestamp lastModified; + + public UpdateCallResponse() { + } +} diff --git a/src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java b/src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java new file mode 100644 index 00000000..d8a61eee --- /dev/null +++ b/src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java @@ -0,0 +1,27 @@ +package com.iemr.common.model.videocall; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.sql.Timestamp; +import lombok.Data; +import com.google.gson.Gson; + +@Data +public class VideoCallRequest { + private Timestamp dateOfCall; + private String callerPhoneNumber; + private String agentID; + private String agentName; + private String meetingLink; + private String callStatus; + private String callDuration; + private Integer providerServiceMapID; + private Long beneficiaryRegID; + private String closureRemark; + private boolean isLinkUsed; + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + private Timestamp linkGeneratedAt; + + public String toJson() { + return new Gson().toJson(this); + } +} diff --git a/src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java b/src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java new file mode 100644 index 00000000..251b877a --- /dev/null +++ b/src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java @@ -0,0 +1,32 @@ +package com.iemr.common.repository.videocall; + +import java.util.List; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.data.jpa.repository.Query; +import com.iemr.common.data.videocall.VideoCallParameters; +import com.iemr.common.model.videocall.VideoCallRequest; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.transaction.annotation.Transactional; + + +@Repository +public interface VideoCallParameterRepository extends CrudRepository { + + @Transactional + @Modifying + @Query("UPDATE VideoCallParameters v SET v.callStatus = :callStatus, v.callDuration = :callDuration, v.modifiedBy = :modifiedBy WHERE v.meetingLink = :meetingLink") + int updateCallStatusByMeetingLink(@Param("meetingLink") String meetingLink, + @Param("callStatus") String callStatus, + @Param("callDuration") String callDuration, + @Param("modifiedBy") String modifiedBy); + + @Query("SELECT v FROM VideoCallParameters v WHERE v.meetingLink = :meetingLink") + VideoCallParameters findByMeetingLink(@Param("meetingLink") String meetingLink); + +} + + + diff --git a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java index 4de605c8..ad9345a1 100644 --- a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java +++ b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java @@ -75,6 +75,7 @@ import com.iemr.common.data.sms.SMSType; import com.iemr.common.data.telemedicine.PrescribedDrugDetail; import com.iemr.common.data.users.User; +import com.iemr.common.data.videocall.VideoCallParameters; import com.iemr.common.mapper.sms.SMSMapper; import com.iemr.common.model.beneficiary.BeneficiaryModel; import com.iemr.common.model.sms.CreateSMSRequest; @@ -100,6 +101,7 @@ import com.iemr.common.repository.sms.SMSTemplateRepository; import com.iemr.common.repository.sms.SMSTypeRepository; import com.iemr.common.repository.users.IEMRUserRepositoryCustom; +import com.iemr.common.repository.videocall.VideoCallParameterRepository; import com.iemr.common.service.beneficiary.IEMRSearchUserService; import com.iemr.common.utils.CryptoUtil; import com.iemr.common.utils.config.ConfigProperties; @@ -165,6 +167,9 @@ public class SMSServiceImpl implements SMSService { @Autowired private TCRequestModelRepo tCRequestModelRepo; + + @Autowired + private VideoCallParameterRepository videoCallParameterRepository; Logger logger = LoggerFactory.getLogger(this.getClass().getName()); @@ -248,23 +253,138 @@ public String getSMSParameters(SMSParameterModel request) throws Exception { return smaParamsResponse.toString(); } + public VideoCallParameters getVideoCallParameters(String meetingLink) { + return videoCallParameterRepository.findByMeetingLink(meetingLink); + } + @Override public String sendSMS(List requests, String authToken) throws Exception { List sentSMS = new ArrayList(); SMSTemplate smsTemplate = null; + for (SMSRequest request : requests) { SMSNotification sms; smsTemplate = smsTemplateRepository.findBySmsTemplateID(request.getSmsTemplateID()); + if (null != smsTemplate && smsTemplate.getSmsTemplateName().equalsIgnoreCase(prescription)) { sentSMS = prepareTMSMS(request, authToken); - } else { + } + else if(null != smsTemplate && smsTemplate.getSmsTemplateName().equalsIgnoreCase("Video Consultation")) + { + String meetingLink = request.getSmsAdvice(); + if (meetingLink == null || meetingLink.isEmpty()) { + throw new Exception("Meeting link is missing in the request"); + } + + VideoCallParameters vcParams = getVideoCallParameters(meetingLink); + if (vcParams == null) { + throw new Exception("Video Call Parameters not found for the provided meeting link: " + meetingLink); + } + + sms = prepareVideoCallSMS(request, vcParams, authToken); + sentSMS.add(sms); + } + else { sms = prepareSMS(request, authToken); sentSMS.add(sms); } } return sentSMS.toString(); } + + public SMSNotification prepareSMSWithVideoCall(SMSRequest request, String authToken, String meetingLink) throws Exception { + VideoCallParameters vcParams = getVideoCallParameters(meetingLink); + + if (vcParams != null) { + return prepareVideoCallSMS(request, vcParams, authToken); + } else { + throw new Exception("Video Call Parameters not found for the provided meeting link: " + meetingLink); + } +} + public SMSNotification prepareVideoCallSMS(SMSRequest request, VideoCallParameters vcParams, String authToken) throws Exception { + + SMSNotification sms = new SMSNotification(); + sms.setSmsStatus(SMSNotification.NOT_SENT); + sms.setCreatedBy(request.getCreatedBy()); + sms.setSmsTriggerDate(new Timestamp(Calendar.getInstance().getTime().getTime())); + sms.setBeneficiaryRegID(request.getBeneficiaryRegID()); + sms.setReceivingUserID(request.getUserID()); + + String smsToSend = ""; + SMSTemplate smsTemplate = smsTemplateRepository.findBySmsTemplateID(request.getSmsTemplateID()); + + if (smsTemplate != null) { + sms.setSmsTemplateID(smsTemplate.getSmsTemplateID()); + smsToSend = smsTemplate.getSmsTemplate(); + + List smsParameters = smsParameterMapRepository + .findSMSParametersMapBySmsTemplateID(request.getSmsTemplateID()); + + for (SMSParametersMap smsParametersMap : smsParameters) { + String variable = smsParametersMap.getSmsParameterName(); + String methodName = smsParametersMap.getSmsParameter().getDataName(); + String variableValue = ""; + variableValue = getVideoCallData(methodName, vcParams); + smsToSend = smsToSend.replace("$$" + variable + "$$", variableValue); + + if ("VideoCall".equalsIgnoreCase(smsParametersMap.getSmsParameter().getSmsParameterType())) { + variableValue = getVideoCallData(methodName, vcParams); + } + + if ("SMS_PHONE_NO".equalsIgnoreCase(variable)) { + sms.setPhoneNo(request.getBenPhoneNo() != null ? request.getBenPhoneNo() : variableValue); + } + else { + smsToSend = smsToSend.replace("$$" + variable + "$$", variableValue); + } + } + + if (request.getAlternateNo() != null) { + sms.setPhoneNo(request.getAlternateNo()); + } + if (request.getFacilityPhoneNo() != null) { + sms.setPhoneNo(request.getFacilityPhoneNo()); + } + } + + sms.setSms(smsToSend); + return smsNotification.save(sms); +} + + + public String getVideoCallData(String methodName, VideoCallParameters videoCall) throws Exception { + String variableValue = ""; + switch (methodName.toLowerCase()) { + case "videoconsultationlink": + variableValue = videoCall.getMeetingLink() != null ? videoCall.getMeetingLink() : ""; + break; + case "consultationdate": + if (videoCall.getDateOfCall() != null) { + SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat outputFormat = new SimpleDateFormat("dd-MM-yyyy h:mm a"); + String formattedDate = outputFormat.format(videoCall.getDateOfCall()); + variableValue = formattedDate; + } + break; + case "phoneno": + variableValue = videoCall.getCallerPhoneNumber() !=null ? videoCall.getCallerPhoneNumber().toString() : ""; + break; + default: + Method method = videoCall.getClass().getDeclaredMethod("get" + capitalize(methodName)); + method.setAccessible(true); + Object result = method.invoke(videoCall); + variableValue = result != null ? result.toString() : ""; + break; + } + + return variableValue.trim(); + } + + private String capitalize(String str) { + return str == null || str.isEmpty() ? str : str.substring(0, 1).toUpperCase() + str.substring(1); + } + // Shubham Shekhar,16-10-2020,TM Prescription SMS private List prepareTMSMS( @@ -691,7 +811,7 @@ private String getBeneficiaryData(String className, String methodName, SMSReques Class clazz = Class.forName(className); Method method = clazz.getDeclaredMethod("get" + methodName, null); variableValue = method.invoke(beneficiary, null).toString(); - break; + break; } return variableValue.replace("null", "").trim(); @@ -967,17 +1087,17 @@ private String getPrescriptionData(String className, String methodName, SMSReque private String getBloodOnCallData(String className, String methodName, SMSRequest request, BeneficiaryModel beneficiary) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException { - T_BloodRequest bloodRequest = prescribedDrugRepository.getBloodRequest(request.getBloodReqID()); - T_RequestedBloodBank requestedBloodBank = prescribedDrugRepository + T_BloodRequest bloodRequest = prescribedDrugRepository.getBloodRequest(request.getBloodReqID()); + T_RequestedBloodBank requestedBloodBank = prescribedDrugRepository .getBloodBankAddress(request.getRequestedBloodBankID()); - String variableValue = ""; + String variableValue = ""; + switch (methodName.toLowerCase()) { case "dateofrequest": - Date requestDate = bloodRequest.getCreatedDate(); - DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - String reportDate = df.format(requestDate); - variableValue = reportDate + " "; - break; + Date requestDate = bloodRequest.getCreatedDate(); + String reportDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(requestDate); + variableValue = reportDate + " "; + break; case "callercontactno": if (request.getIsBloodBankSMS() == true) { variableValue = (beneficiary.getBenPhoneMaps().size() > 0 @@ -1034,9 +1154,10 @@ private String getBloodOnCallData(String className, String methodName, SMSReques private String getDirectoryserviceData(String className, String methodName, SMSRequest request) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - Directoryservice directoryservice = prescribedDrugRepository + Directoryservice directoryservice = prescribedDrugRepository .getDirectoryservice(request.getDirectoryServiceID()); - String variableValue = ""; + String variableValue = ""; + if(null != directoryservice && null != directoryservice.getInstitute()) { switch (methodName.toLowerCase()) { case "institutename": diff --git a/src/main/java/com/iemr/common/service/videocall/VideoCallService.java b/src/main/java/com/iemr/common/service/videocall/VideoCallService.java new file mode 100644 index 00000000..9322050b --- /dev/null +++ b/src/main/java/com/iemr/common/service/videocall/VideoCallService.java @@ -0,0 +1,13 @@ +package com.iemr.common.service.videocall; +import com.iemr.common.utils.response.OutputResponse; +import com.iemr.common.model.videocall.UpdateCallRequest; +import com.iemr.common.model.videocall.VideoCallRequest; + +public interface VideoCallService { + + public String generateMeetingLink() throws Exception; + + public String sendMeetingLink(VideoCallRequest request) throws Exception; + + public String updateCallStatus(UpdateCallRequest request) throws Exception; +} diff --git a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java new file mode 100644 index 00000000..d1cf7000 --- /dev/null +++ b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java @@ -0,0 +1,130 @@ +package com.iemr.common.service.videocall; + +import org.apache.commons.lang.RandomStringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.sql.Timestamp; +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.io.IOException; +import com.iemr.common.data.videocall.VideoCallParameters; +import com.iemr.common.mapper.videocall.VideoCallMapper; +import com.iemr.common.model.videocall.UpdateCallRequest; +import com.iemr.common.model.videocall.VideoCallRequest; +import com.iemr.common.repository.videocall.VideoCallParameterRepository; +import com.iemr.common.utils.config.ConfigProperties; +import com.iemr.common.utils.mapper.OutputMapper; +import com.iemr.common.utils.response.OutputResponse; + +@Service +public class VideoCallServiceImpl implements VideoCallService { + final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + @Autowired + private VideoCallParameterRepository videoCallRepository; + + @Autowired + private VideoCallMapper videoCallMapper; + + private String meetingLink; + private boolean isLinkSent = false; + private String consultationStatus = "Not Initiated"; + private String jitsiLink; + + public VideoCallServiceImpl() { + this.jitsiLink = ConfigProperties.getPropertyByName("video-call-url"); + logger.info("Jitsi Link fetched: " + this.jitsiLink); + } + + @Override + public String generateMeetingLink() { + meetingLink=jitsiLink+RandomStringUtils.randomAlphanumeric(8); + logger.info("Meeting link: " + meetingLink); + return meetingLink; + } + + @Override + public String sendMeetingLink(VideoCallRequest request) throws Exception { + OutputResponse response = new OutputResponse(); + + if (meetingLink == null || meetingLink.isEmpty()) { + throw new Exception("Meeting link not generated yet."); + } + + isLinkSent = true; + + VideoCallParameters videoCallEntity = videoCallMapper.videoCallToEntity(request); + videoCallEntity.setMeetingLink(meetingLink); + videoCallEntity.setLinkGeneratedAt(Timestamp.valueOf(LocalDateTime.now())); + videoCallEntity.setLinkUsed(false); + + videoCallRepository.save(videoCallEntity); + + VideoCallRequest responseData = videoCallMapper.videoCallToRequest(videoCallEntity); + response.setResponse(responseData.toJson()); + + return OutputMapper.gsonWithoutExposeRestriction() + .toJson(response); + } + +@Override +public String updateCallStatus(UpdateCallRequest callRequest) throws Exception { + VideoCallParameters videoCall = null; + + VideoCallParameters requestEntity = videoCallMapper.updateRequestToVideoCall(callRequest); + + videoCall = videoCallRepository.findByMeetingLink(requestEntity.getMeetingLink()); + + int updateCount = videoCallRepository.updateCallStatusByMeetingLink( + requestEntity.getMeetingLink(), + requestEntity.getCallStatus(), + requestEntity.getCallDuration(), + requestEntity.getModifiedBy() + ); + + if (updateCount > 0) { + videoCall.setLinkUsed(true); + videoCallRepository.save(videoCall); + + // if ("Completed".equalsIgnoreCase(requestEntity.getCallStatus())) { + // saveRecordingFile(videoCall.getMeetingLink()); + // } + } else { + throw new Exception("Failed to update the call status"); + } + + return OutputMapper.gsonWithoutExposeRestriction() + .toJson(videoCallMapper.videoCallToResponse(videoCall)); +} +private void saveRecordingFile(String meetingLink) { + try { + // Configurable Jibri recording location + String jibriOutputDir = ConfigProperties.getPropertyByName("jibri.output.path"); // e.g., /srv/jibri/recordings + String saveDir = ConfigProperties.getPropertyByName("video.recording.path"); // e.g., /srv/recordings + + File jibriDir = new File(jibriOutputDir); + File[] matchingFiles = jibriDir.listFiles((dir, name) -> name.contains(meetingLink) && name.endsWith(".mp4")); + + if (matchingFiles != null && matchingFiles.length > 0) { + File recording = matchingFiles[0]; + Path targetPath = Paths.get(saveDir, meetingLink + ".mp4"); + + Files.copy(recording.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING); + logger.info("Recording file saved: " + targetPath); + } else { + logger.warn("No matching recording file found for meeting: " + meetingLink); + } + } catch (IOException e) { + logger.error("Error saving recording file: ", e); + } +} + +} + + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1f3cdc32..813637b0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -19,10 +19,10 @@ spring.http.multipart.max-request-size=1000MB spring.http.multipart.max-file-size=1000MB ##sms details for CHO -CHOSmsTemplate= CHO UPTSU SMS +CHOSmsTemplate=CHO UPTSU SMS ##sms details for beneficiary -BeneficiarySmsTemplate= Beneficiary UPTSU SMS +BeneficiarySmsTemplate=Beneficiary UPTSU SMS ######Project specific settings future-days=7 @@ -136,7 +136,6 @@ logging.level.org.springframework.web=INFO logging.level.org.hibernate=INFO logging.level.com.iemr=DEBUG logging.level.org.springframework=INFO - #If both properties are set, only logging.file.name takes effect. logging.logback.rollingpolicy.max-file-size=20KB @@ -147,7 +146,7 @@ sms-message-type=SERVICE_EXPLICIT sms-entityid=1201161708885589464 ### generate Beneficiary IDs URL -generateBeneficiaryIDs-api-url =/generateBeneficiaryController/generateBeneficiaryIDs +generateBeneficiaryIDs-api-url=/generateBeneficiaryController/generateBeneficiaryIDs # CTI Config call-info-api-URL=http://CTI_SERVER/apps/appsHandler.php?transaction_id=CTI_CDR_CALL_INFO&agent_id=AGENT_ID&session_id=SESSION_ID&phone_num=PHONE_NO&resFormat=3 @@ -173,3 +172,4 @@ failedLoginAttempt=5 #Jwt Token configuration jwt.access.expiration=86400000 jwt.refresh.expiration=604800000 + From 327321c8d795baacda83497dd9387fcf5ffc385c Mon Sep 17 00:00:00 2001 From: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:13:15 +0530 Subject: [PATCH 2/5] fix:build issue (#230) --- .../iemr/common/controller/videocall/VideoCallController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java b/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java index 2b0c48d4..8eb2a3ad 100644 --- a/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java +++ b/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java @@ -34,7 +34,6 @@ public class VideoCallController { @Autowired private VideoCallService videoCallService; - @CrossOrigin() @PostMapping(value = "/generate-link", produces = MediaType.APPLICATION_JSON_VALUE, headers = "Authorization") public ResponseEntity> generateJitsiLink() { Map response = new HashMap<>(); From 15ecbb536534a7c97e76a50fc06eff8457ff10ee Mon Sep 17 00:00:00 2001 From: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Date: Mon, 23 Jun 2025 14:04:10 +0530 Subject: [PATCH 3/5] Change the value for meeting link and enable the grievance scheduler (#233) --- src/main/environment/common_ci.properties | 2 +- .../common/service/videocall/VideoCallServiceImpl.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 1bcf2b13..8d42a126 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -170,7 +170,7 @@ springdoc.swagger-ui.enabled=false isProduction=@env.IS_PRODUCTION@ grievanceAllocationRetryConfiguration=3 -start-grievancedatasync-scheduler=false +start-grievancedatasync-scheduler=true cron-scheduler-grievancedatasync=0 0/2 * * * ? captcha.secret-key=@env.CAPTCHA_SECRET_KEY@ diff --git a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java index d1cf7000..6806834d 100644 --- a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java +++ b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java @@ -21,6 +21,7 @@ import com.iemr.common.utils.config.ConfigProperties; import com.iemr.common.utils.mapper.OutputMapper; import com.iemr.common.utils.response.OutputResponse; +import org.springframework.beans.factory.annotation.Value; @Service public class VideoCallServiceImpl implements VideoCallService { @@ -32,14 +33,15 @@ public class VideoCallServiceImpl implements VideoCallService { @Autowired private VideoCallMapper videoCallMapper; + @Value("${video-call-url}") private String meetingLink; + private boolean isLinkSent = false; private String consultationStatus = "Not Initiated"; private String jitsiLink; - public VideoCallServiceImpl() { - this.jitsiLink = ConfigProperties.getPropertyByName("video-call-url"); - logger.info("Jitsi Link fetched: " + this.jitsiLink); + // this.jitsiLink = ConfigProperties.getPropertyByName("video-call-url"); + // logger.info("Jitsi Link fetched: " + this.jitsiLink); } @Override From 983dfd10849b090c1038b1f234eed5705db52188 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Mon, 23 Jun 2025 18:19:09 +0530 Subject: [PATCH 4/5] Remove firebase base64 variable --- src/main/environment/common_ci.properties | 2 -- src/main/environment/common_docker.properties | 2 -- .../common/config/firebase/FirebaseMessagingConfig.java | 7 +------ 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 9eac3447..50be0c7f 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -44,8 +44,6 @@ cron-scheduler-sms=0 0/1 * * * ? * firebase.enabled=@env.FIREBASE_ENABLE@ # if using file firebase.credential-file=@env.FIREBASE_CREDENTIAL@ -# for CI/CD -firebase.credential-base64=@env.CREDENTIAL_BASE64@ #### Email Configuration send-email=@env.SEND_EMAIL@ diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 7fc32ba3..8989ac1d 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -184,5 +184,3 @@ cors.allowed-origins=${CORS_ALLOWED_ORIGINS} firebase.enabled=${FIREBASE_ENABLE} # if using file firebase.credential-file=${FIREBASE_CREDENTIAL} -# for CI/CD -firebase.credential-base64=${CREDENTIAL_BASE64} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/config/firebase/FirebaseMessagingConfig.java b/src/main/java/com/iemr/common/config/firebase/FirebaseMessagingConfig.java index 438ca4f9..250558bb 100644 --- a/src/main/java/com/iemr/common/config/firebase/FirebaseMessagingConfig.java +++ b/src/main/java/com/iemr/common/config/firebase/FirebaseMessagingConfig.java @@ -22,8 +22,6 @@ public class FirebaseMessagingConfig { @Value("${firebase.credential-file:}") private String firebaseCredentialFile; - @Value("${firebase.credential-base64:}") - private String firebaseCredentialBase64; @Bean public FirebaseMessaging firebaseMessaging() throws IOException { @@ -33,10 +31,7 @@ public FirebaseMessaging firebaseMessaging() throws IOException { GoogleCredentials credentials; - if (!firebaseCredentialBase64.isBlank()) { - byte[] decoded = Base64.getDecoder().decode(firebaseCredentialBase64); - credentials = GoogleCredentials.fromStream(new ByteArrayInputStream(decoded)); - } else if (!firebaseCredentialFile.isBlank()) { + if (!firebaseCredentialFile.isBlank()) { credentials = GoogleCredentials.fromStream( new ClassPathResource(firebaseCredentialFile).getInputStream() ); From bc777cbeb07541a4462e2250ae3551c1c015b2ba Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Mon, 23 Jun 2025 18:39:49 +0530 Subject: [PATCH 5/5] Remove firebase base64 variable --- src/main/environment/common_docker.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 8989ac1d..e490991b 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -184,3 +184,4 @@ cors.allowed-origins=${CORS_ALLOWED_ORIGINS} firebase.enabled=${FIREBASE_ENABLE} # if using file firebase.credential-file=${FIREBASE_CREDENTIAL} +