import { Injectable , OnModuleInit } from '@nestjs/common' ; import { AchievementRepository } from '../repository/achievement.repository' ; import { StartEventShareCourseRuleDTO } from '../dto/start-event-share-course.dto' ; import { EventNameEnum } from '../enum/event-name.enum' ; import { BadgeRepository } from '../repository/badge.repository' ; import * as PubSub from 'pubsub-js' ; import { CourseTaken } from '../../CourseModule/entity/course-taken.entity' ; import { Achievement } from '../entity/achievement.entity' ; import { StartEventRateAppRuleDTO } from '../dto/start-event-rate-app.dto' ; import { User } from '../../UserModule/entity/user.entity' ; import { Badge } from '../entity/badge.entity' ; import { UserRepository } from '../../UserModule/repository/user.repository' ; import { CourseTakenRepository } from '../../CourseModule/repository/course.taken.repository' ; import { ShareAppRewardDataDTO } from '../dto/share-app-reward-data.dto' ; import { SchedulerRegistry } from '@nestjs/schedule' ; import { OrderEnum } from '../../CommonsModule/enum/order.enum' ; import { TimeRangeEnum } from '../enum/time-range.enum' ; import { RankingQueryDTO } from '../dto/ranking-query.dto' ; export interface SharedCourseRule { courseId : number ; } export interface InviteUserRewardData { inviteKey : string ; } @ Injectable ( ) export class UserRewardsService implements OnModuleInit { constructor ( private readonly achievementRepository , private readonly badgeRepository , private readonly userRepository , private readonly courseTakenRepository , private readonly schedulerRegistry ) { } onModuleInit ( ) { PubSub . subscribe ( EventNameEnum . USER_REWARD_SHARE_COURSE , async ( message , data ) => { await this . shareCourseReward ( data ) ; } ) ; PubSub . subscribe ( EventNameEnum . USER_REWARD_RATE_APP , async ( message , data ) => { await this . rateAppReward ( data ) ; } ) ; PubSub . subscribe ( EventNameEnum . USER_REWARD_INVITE_USER , async ( message , data ) => { await this . inviteUserReward ( data ) ; } ) ; PubSub . subscribe ( EventNameEnum . USER_REWARD_COMPLETE_REGISTRATION , async ( message , data ) => { await this . completeRegistrationReward ( data ) ; } ) ; PubSub . subscribe ( EventNameEnum . USER_REWARD_SHARE_APP , async ( message , data ) => { await this . shareAppReward ( data ) ; } ) ; PubSub . subscribe ( '*' , async ( ) => { const alreadyRanTopRankingThisMonth = await this . alreadyRanTopRankingLastMonth ( ) ; if ( alreadyRanTopRankingThisMonth ) return ; const tenSecondsInMilliseconds = 60000 ; const callback = ( ) => { this . topRankingMonthlyReward ( ) ; } ; const timeout = setTimeout ( callback , tenSecondsInMilliseconds ) ; this . schedulerRegistry . addTimeout ( `TOP_RANKING_MONTHLY${ new Date ( ) . getTime ( ) }` , timeout ) ; } ) ; } private async shareCourseReward ( { courseId , userId , platform , } ) { const courseTaken = await this . courseTakenRepository . findCompletedByUserIdAndCourseId ( userId , courseId ) ; if ( ! courseTaken ) return ; const [ sharedCourse , ] = await this . achievementRepository . getSharedCourseByCourseIdAndUserIdAndSocialMedia < Achievement < SharedCourseRule > > ( courseId , userId , platform ) ; if ( sharedCourse ) return ; const badge = await this . badgeRepository . findByEventNameAndOrder ( EventNameEnum . USER_REWARD_SHARE_COURSE , 1 ) ; await this . achievementRepository . save ( { ... sharedCourse , user : { id : userId } , badge , rule : { courseId , platform } , completed : true , eventName : EventNameEnum . USER_REWARD_SHARE_COURSE , points : badge . points , } ) ; } private async rateAppReward ( { userId , rate } ) { const response = await this . userRepository . find ( { where : { id : userId } , } ) ; const user = response [ 0 ] ; if ( ! user ) return ; const badge = await this . badgeRepository . findByEventNameAndOrder ( EventNameEnum . USER_REWARD_RATE_APP , 1 ) ; if ( ! badge ) return ; const achievement = await this . achievementRepository . findByUserIdAndBadgeId ( user . id , badge . id ) ; if ( achievement ) return ; await this . achievementRepository . save ( { ... achievement , badge , user , eventName : EventNameEnum . USER_REWARD_RATE_APP , completed : true , rule : { rate } , points : badge . points , } ) ; } private async inviteUserReward ( { inviteKey , } ) { const userWithGivenInviteKey = await this . userRepository . findByInviteKey ( inviteKey ) ; if ( ! userWithGivenInviteKey ) return ; const usersInvitedCount = await this . userRepository . countUsersInvitedByUserId ( userWithGivenInviteKey . id ) ; const neededAchievementsQuantity = Math . trunc ( usersInvitedCount / 3 ) ; if ( neededAchievementsQuantity === 0 ) return ; const inviteUsersBadge = await this . badgeRepository . findByEventNameAndOrder ( EventNameEnum . USER_REWARD_INVITE_USER , 1 ) ; if ( ! inviteUsersBadge ) return ; const countInviteUserAchievements = await this . achievementRepository . countAchievementsByBadgeId ( inviteUsersBadge . id ) ; if ( neededAchievementsQuantity === countInviteUserAchievements ) return ; const achievementsDifference = neededAchievementsQuantity - countInviteUserAchievements ; for ( let counter = 0 ; counter < achievementsDifference ; counter ++ ) { await this . achievementRepository . save ( { badge : inviteUsersBadge , user : userWithGivenInviteKey , eventName : EventNameEnum . USER_REWARD_INVITE_USER , completed : true , points : inviteUsersBadge . points , } ) ; } } private async completeRegistrationReward ( { id } ) { const response = await this . userRepository . find ( { where : { id } } ) ; const user = response [ 0 ] ; if ( ! user ) return ; const propertiesToCheck = [ 'address' , 'institutionName' , 'gender' , 'birthday' , 'nickname' , 'cep' , 'houseNumber' , 'phone' , 'state' , ] ; let isProfileComplete = true ; propertiesToCheck . forEach ( ( propertyName ) => { if ( user [ propertyName ] == null ) isProfileComplete = false ; } ) ; if ( ! isProfileComplete ) return ; const completeRegistrationBadge = await this . badgeRepository . findByEventNameAndOrder ( EventNameEnum . USER_REWARD_COMPLETE_REGISTRATION , 1 ) ; const achievement = await this . achievementRepository . findCompletedByUserIdAndBadgeId ( user . id , completeRegistrationBadge . id ) ; if ( achievement ) return ; await this . achievementRepository . save ( { badge : completeRegistrationBadge , user , eventName : EventNameEnum . USER_REWARD_COMPLETE_REGISTRATION , completed : true , points : completeRegistrationBadge . points , } ) ; } private async shareAppReward ( { userId , platform , } ) { const queryResponse = await this . userRepository . find ( { where : { id : userId } , } ) ; const user = queryResponse [ 0 ] ; if ( ! user ) return ; const responses = await this . achievementRepository . sharedAppThisWeek ( userId ) ; if ( responses . length > 3 ) return ; const shareAppBadge = await this . badgeRepository . findByEventNameAndOrder ( EventNameEnum . USER_REWARD_SHARE_APP , 1 ) ; if ( ! shareAppBadge ) return ; await this . achievementRepository . save ( { badge : shareAppBadge , user , rule : { platform } , eventName : EventNameEnum . USER_REWARD_SHARE_APP , completed : true , points : shareAppBadge . points , } ) ; } private async topRankingMonthlyReward ( ) { const [ user , ] = await this . achievementRepository . getLastTimeRangeRanking ( OrderEnum . DESC , TimeRangeEnum . MONTH , 1 ) ; if ( ! user ) return ; const badge = await this . badgeRepository . findByEventNameAndOrder ( EventNameEnum . USER_REWARD_TOP_MONTH , 1 ) ; const alreadyRanTopRankingThisMonth = await this . alreadyRanTopRankingLastMonth ( ) ; if ( alreadyRanTopRankingThisMonth ) return ; await this . achievementRepository . save ( { eventName : EventNameEnum . USER_REWARD_TOP_MONTH , badge , user : { id : user . userId } , rule : { month : new Date ( ) . getMonth ( ) === 0 ? 12 : new Date ( ) . getMonth ( ) + 1 , year : new Date ( ) . getMonth ( ) === 0 ? new Date ( ) . getFullYear ( ) - 1 : new Date ( ) . getFullYear ( ) , } , completed : true , points : badge . points , } ) ; } private async alreadyRanTopRankingLastMonth ( ) { const response = await this . achievementRepository . checkIfHasTopRankForLastMonth ( ) ; return response . length > 0 ; } }