Rev 37388 | Go to most recent revision | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.service;import com.google.gson.Gson;import com.spice.profitmandi.common.enumuration.MessageType;import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;import com.spice.profitmandi.common.model.CustomRetailer;import com.spice.profitmandi.common.model.ProfitMandiConstants;import com.spice.profitmandi.common.model.SendNotificationModel;import com.spice.profitmandi.dao.entity.auth.AuthUser;import com.spice.profitmandi.dao.entity.dtr.*;import com.spice.profitmandi.dao.entity.fofo.Customer;import com.spice.profitmandi.dao.entity.user.Device;import com.spice.profitmandi.dao.entity.whatsapp.WhatsappMessage;import com.spice.profitmandi.dao.model.SimpleCampaign;import com.spice.profitmandi.dao.model.SimpleCampaignParams;import com.spice.profitmandi.dao.repository.catalog.DeviceRepository;import com.spice.profitmandi.dao.repository.cs.CsService;import com.spice.profitmandi.dao.repository.cs.PartnerRegionRepository;import com.spice.profitmandi.dao.repository.dtr.*;import com.spice.profitmandi.dao.repository.fofo.CustomerRepository;import com.spice.profitmandi.dao.repository.whatsapp.WhatsappMessageRepository;import com.spice.profitmandi.service.user.RetailerService;import com.spice.profitmandi.service.whatsapp.WhatsappFlow;import com.spice.profitmandi.service.whatsapp.WhatsappMessageService;import com.spice.profitmandi.service.whatsapp.WhatsappMessageType;import com.spice.profitmandi.service.whatsapp.WhatsappNumbers;import com.spice.profitmandi.service.whatsapp.BotPenguinWhatsappService;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import java.nio.charset.StandardCharsets;import java.time.LocalDate;import java.time.LocalDateTime;import java.util.*;import java.util.stream.Collectors;@Componentpublic class NotificationServiceImpl implements NotificationService {private static final Logger LOGGER = LogManager.getLogger(NotificationServiceImpl.class);// Region id that represents "all partners". Schemes meant for everyone are tagged to this region;// partner_region is not populated for it, so it must be expanded to all partners here.private static final int ALL_PARTNERS_REGION = 5;@AutowiredUserCampaignRepository userCampaignRepository;@AutowiredUserRepository dtrUserRepository;@AutowiredUserAccountRepository userAccountRepository;@AutowiredNotificationCampaignRepository notificationCampaignRepository;@AutowiredDeviceRepository deviceRepository;@AutowiredPartnerRegionRepository partnerRegionRepository;@AutowiredFofoStoreRepository fofoStoreRepository;@AutowiredCsService csService;@AutowiredRetailerService retailerService;@AutowiredCustomerRepository customerRepository;@AutowiredPushNotificationRepository pushNotificationRepository;@Autowiredprivate Gson gson;@Value("${prod}")private boolean isProd;@Autowiredprivate WhatsappMessageRepository whatsappMessageRepository;@Autowiredprivate WhatsappMessageService whatsappMessageService;@Autowiredprivate BotPenguinWhatsappService botPenguin;@Overridepublic void sendNotification(SendNotificationModel sendNotificationModel) throws ProfitMandiBusinessException {SimpleCampaignParams scp = new SimpleCampaignParams();scp.setMessage(sendNotificationModel.getMessage());scp.setTitle(sendNotificationModel.getTitle());scp.setImageUrl(sendNotificationModel.getImageUrl());scp.setType(sendNotificationModel.getType());scp.setUrl(sendNotificationModel.getUrl());scp.setShowImage(sendNotificationModel.getShowImage());scp.setExpireTimestamp(sendNotificationModel.getExpiresat());SimpleCampaign sc = new SimpleCampaign(scp);sc.setSimpleCampaignParams(scp);NotificationCampaign nc = new NotificationCampaign();nc.setName(sendNotificationModel.getCampaignName());nc.setImplementationType("SimpleCampaign");nc.setImplementationParams(gson.toJson(scp));nc.setMessageType(sendNotificationModel.getMessageType());nc.setDocumentId(sendNotificationModel.getDocumentId());nc.setCreatedTimestamp(LocalDateTime.now());notificationCampaignRepository.persist(nc);Set<Integer> userIds = new HashSet<>();if (sendNotificationModel.getUserIds() != null && sendNotificationModel.getUserIds().size() > 0) {userIds.addAll(sendNotificationModel.getUserIds());}if (sendNotificationModel.getStateIds() != null && sendNotificationModel.getStateIds().size() > 0) {List<Integer> fofoIds = fofoStoreRepository.selectByWarehouseIds(sendNotificationModel.getStateIds()).stream().map(x -> x.getId()).collect(Collectors.toList());if (fofoIds.size() > 0) {userIds.addAll(userAccountRepository.selectUserIdsByRetailerIds(fofoIds));}}if(sendNotificationModel.getRegionIds() != null && sendNotificationModel.getRegionIds().size() > 0) {if (sendNotificationModel.getRegionIds().contains(ALL_PARTNERS_REGION)) {// "ALL partners" region: partner_region only holds a sentinel, so expand to every partner.userIds.addAll(fofoStoreRepository.selectAllDtrUserIds());} else {List<Integer> fofoIds = partnerRegionRepository.selectAllByRegionIds(sendNotificationModel.getRegionIds()).stream().map(x->x.getFofoId()).collect(Collectors.toList());if(fofoIds.size()>0) {userIds.addAll(userAccountRepository.selectUserIdsByRetailerIds(fofoIds));}}}if (userIds.size() > 0) {for (Integer userId : userIds) {UserCampaign uc = new UserCampaign();uc.setCampaignId(nc.getId());uc.setUserId(userId);uc.setPushTimestamp(LocalDateTime.now());userCampaignRepository.persist(uc);}List<Device> devices = deviceRepository.selectByUserIdAndModifiedTimestamp(new ArrayList<>(userIds),LocalDateTime.now().minusMonths(1), LocalDateTime.now());pushNotification(nc.getId(), devices);} else {LOGGER.info("Failed to send notification to any retailer with this model - {}", sendNotificationModel);}}@Overridepublic void sendNotificationToAll(SendNotificationModel sendNotificationModel) throws ProfitMandiBusinessException {sendNotificationModel.setUserIds(fofoStoreRepository.selectAllDtrUserIds());Set<AuthUser> authUsers = new HashSet<>(csService.getAuthUserByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_RBM));authUsers.addAll(csService.getAuthUserByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_CATEGORY));authUsers.addAll(csService.getAuthUserByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_SALES));authUsers.addAll(csService.getAuthUserByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_ABM));List<String> emailIds = authUsers.stream().map(x -> x.getEmailId()).collect(Collectors.toList());emailIds.add("devkinandan.lal@smartdukaan.com");List<User> systemUsers = dtrUserRepository.selectAllByEmailIds(emailIds);sendNotificationModel.getUserIds().addAll(systemUsers.stream().map(x -> x.getId()).collect(Collectors.toList()));this.sendNotification(sendNotificationModel);}@Overridepublic void sendNotificationToSystemUsers(SendNotificationModel sendNotificationModel) throws ProfitMandiBusinessException {Set<AuthUser> authUsers = new HashSet<>(csService.getAuthUserByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_RBM));authUsers.addAll(csService.getAuthUserByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_CATEGORY));authUsers.addAll(csService.getAuthUserByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_SALES));authUsers.addAll(csService.getAuthUserByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_ABM));List<String> emailIds = authUsers.stream().map(x -> x.getEmailId()).collect(Collectors.toList());emailIds.add("devkinandan.lal@smartdukaan.com");List<User> systemUsers = dtrUserRepository.selectAllByEmailIds(emailIds);sendNotificationModel.setUserIds(systemUsers.stream().map(x -> x.getId()).collect(Collectors.toList()));this.sendNotification(sendNotificationModel);}@Overridepublic void sendNotification(int fofoId, String campaignName, MessageType messageType, String title, String message)throws ProfitMandiBusinessException {SendNotificationModel sendNotificationModel = this.getDefaultNotificationModel();sendNotificationModel.setCampaignName(campaignName);sendNotificationModel.setMessageType(messageType);sendNotificationModel.setTitle(title);sendNotificationModel.setMessage(message);int userId = userAccountRepository.selectUserIdByRetailerId(fofoId);sendNotificationModel.setUserIds(Arrays.asList(userId));sendNotificationModel.setMessageType(MessageType.wallet);this.sendNotification(sendNotificationModel);}public SendNotificationModel getDefaultNotificationModel() {SendNotificationModel sendNotificationModel = new SendNotificationModel();sendNotificationModel.setType("url");sendNotificationModel.setUrl("https://app.smartdukaan.com/pages/home/notifications");sendNotificationModel.setExpiresat(LocalDateTime.now().plusDays(1));sendNotificationModel.setMessageType(MessageType.notification);return sendNotificationModel;}public void pushNotification(int cid, List<Device> devices) {for (Device device : devices) {PushNotifications pn = new PushNotifications();pn.setNotificationCampaignid(cid);pn.setDeviceId(device.getId());pn.setUserId(device.getUser_id());pushNotificationRepository.persist(pn);}}@Overridepublic void sendNotification(int fofoId, String campaignName, MessageType messageType, String title, String message, String url) throws ProfitMandiBusinessException {SendNotificationModel sendNotificationModel = this.getDefaultNotificationModel();sendNotificationModel.setCampaignName(campaignName);sendNotificationModel.setMessageType(messageType);sendNotificationModel.setTitle(title);sendNotificationModel.setMessage(message);int userId = userAccountRepository.selectUserIdByRetailerId(fofoId);sendNotificationModel.setUserIds(Arrays.asList(userId));sendNotificationModel.setMessageType(messageType);sendNotificationModel.setUrl(url);this.sendNotification(sendNotificationModel);}/*** Invoice PDF, sent as the template's document header.** <p>The upsell wording that used to be built here now lives in the approved template; only the* recipient's name and the PDF link cross the wire.</p>*/@Overridepublic boolean sendWhatsappInvoice(int customerId, int fofoId, String invoiceNumber, String whatsAppNo) throws Exception {if (!shouldSendWhatsappMessage(whatsAppNo)) {return false;}Customer customer = customerRepository.selectById(customerId);String mobileNumber = (whatsAppNo != null && !whatsAppNo.isEmpty()) ? whatsAppNo : customer.getMobileNumber();String mediaUrl = "https://partners.smartdukaan.com/wa-invoice-send/"+ Base64.getMimeEncoder().encodeToString(invoiceNumber.getBytes(StandardCharsets.UTF_8)) + ".pdf";String fileName = "INV-" + invoiceNumber.replace("/", "-") + ".pdf";LOGGER.info("sendWhatsappInvoice: mobile={} mediaUrl={}", mobileNumber, mediaUrl);return botPenguin.send(WhatsappFlow.INVOICE, mobileNumber,Arrays.asList(customerName(customer), storeName(fofoId)), mediaUrl, fileName,WhatsappMessageType.DOCUMENT);}@Overridepublic boolean sendPaymentLink(String mobile, String paymentLink) throws Exception {return botPenguin.send(WhatsappFlow.PAYMENT_LINK, mobile,Collections.singletonList(paymentLink), null, null, null);}@Overridepublic boolean sendOfferAnnouncement(String mobile, String offerName, String schemeType, String startDate,String endDate, String imageUrl) throws Exception {return botPenguin.send(WhatsappFlow.OFFER, mobile,Arrays.asList(offerName, schemeType, startDate, endDate), imageUrl, null,WhatsappMessageType.IMAGE);}@Overridepublic boolean sendBiddingLive(String mobile, List<String> lots, String mediaUrl) throws Exception {int slots = ProfitMandiConstants.WHATSAPP_TEMPLATE.BIDDING_LIVE.getBodyParamCount();if (lots.size() > slots) {LOGGER.warn("sendBiddingLive: {} lots available but the template holds {}; dropping {}",lots.size(), slots, lots.subList(slots, lots.size()));}List<String> params = new ArrayList<>(lots.subList(0, Math.min(lots.size(), slots)));// Meta rejects an empty parameter, so unused slots carry a dash rather than "".while (params.size() < slots) {params.add("-");}return botPenguin.send(WhatsappFlow.BIDDING_LIVE, mobile, params, mediaUrl, null,WhatsappMessageType.IMAGE);}@Overridepublic boolean sendLoanDefaultAlert(String mobile, String name, String outstandingAmount) throws Exception {LOGGER.info("Is Prod - {}", isProd);if (!isProd) {return false;}return botPenguin.send(WhatsappFlow.LOAN_ALERT, mobile,Arrays.asList(name, outstandingAmount), null, null, null);}@Overridepublic boolean sendOrderDelivered(String mobile, String airwayBillNumber) throws Exception {LOGGER.info("Is Prod - {}", isProd);if (!isProd) {return false;}return botPenguin.send(WhatsappFlow.ORDER_DELIVERED, mobile,Collections.singletonList(airwayBillNumber), null, null, null);}@Overridepublic boolean sendADLDWhatsappMessage(String mobile, String invoiceNumber, String mediaUrl,String fileName) throws Exception {LOGGER.info("Is Prod - {}", isProd);if (!isProd) {return false;}return botPenguin.send(WhatsappFlow.ADLD, mobile, Collections.singletonList(invoiceNumber),mediaUrl, fileName, WhatsappMessageType.DOCUMENT);}@Overridepublic boolean shouldSendWhatsappMessage(String mobile) {// Normalised rather than concatenated: BotPenguin stores the normalised MSISDN, so for anything// that is not already a bare ten digits the old "91" + mobile key could never match a stored row// and the throttle quietly never fired. Identical result for clean input.String destAddr = WhatsappNumbers.toMsisdn(mobile);if (destAddr == null) {return true;}boolean shouldSend = true;List<WhatsappMessage> whatsappMessages = whatsappMessageRepository.selectByDestAddr(destAddr, LocalDate.now());if (!whatsappMessages.isEmpty()) {long failedCount = whatsappMessages.stream().filter(x -> x.getFailed() != null && x.getFailed().equals("FAILED")).collect(Collectors.counting());if (failedCount >= 2) {shouldSend = false;}}return shouldSend;}/*** The store the purchase was made at, for the invoice template's second variable. Falls back to the* brand name: an unknown partner id must not stop an invoice going out.*/private String storeName(int fofoId) {try {CustomRetailer retailer = retailerService.getFofoRetailer(fofoId);String businessName = retailer == null ? null : retailer.getBusinessName();if (businessName != null && !businessName.trim().isEmpty()) {return businessName.trim();}} catch (Exception e) {LOGGER.warn("storeName: could not resolve fofoId {}", fofoId, e);}return "SmartDukaan";}/** Meta greets by name, so an empty first name would render "Hello ,". */private static String customerName(Customer customer) {String firstName = customer == null ? null : customer.getFirstName();return (firstName == null || firstName.trim().isEmpty()) ? "Customer" : firstName.trim();}}