From 83e4f34d3c3f03bbb1096490fb22f59e8058826d Mon Sep 17 00:00:00 2001 From: harisato Date: Thu, 19 Oct 2023 15:45:38 +0700 Subject: [PATCH] feat(be): add get quest detail api --- .../punkga-pg/tables/public_user_quest.yaml | 8 ++ .../tables/public_user_quest_reward.yaml | 4 + src/quest/dto/get-campaign-quest.dto.ts | 11 +++ src/quest/quest.controller.ts | 16 +++- src/quest/quest.module.ts | 15 +++- src/quest/quest.service.ts | 86 +++++++++++++++++-- src/repeat-quests/repeat-quests.graphql.ts | 30 +++++++ src/repeat-quests/repeat-quests.module.ts | 11 +++ .../social-activities.graphql.ts | 30 +++++++ .../social-activities.module.ts | 11 +++ src/subscribers/subscribers.graphql.ts | 30 +++++++ src/subscribers/subscribers.module.ts | 11 +++ src/user-quests/user-quests.graphql.ts | 33 +++++++ src/user-quests/user-quests.module.ts | 11 +++ 14 files changed, 299 insertions(+), 8 deletions(-) create mode 100644 src/quest/dto/get-campaign-quest.dto.ts create mode 100644 src/repeat-quests/repeat-quests.graphql.ts create mode 100644 src/repeat-quests/repeat-quests.module.ts create mode 100644 src/social-activites/social-activities.graphql.ts create mode 100644 src/social-activites/social-activities.module.ts create mode 100644 src/subscribers/subscribers.graphql.ts create mode 100644 src/subscribers/subscribers.module.ts create mode 100644 src/user-quests/user-quests.graphql.ts create mode 100644 src/user-quests/user-quests.module.ts diff --git a/hasura/metadata/databases/punkga-pg/tables/public_user_quest.yaml b/hasura/metadata/databases/punkga-pg/tables/public_user_quest.yaml index efa098eb..922944de 100644 --- a/hasura/metadata/databases/punkga-pg/tables/public_user_quest.yaml +++ b/hasura/metadata/databases/punkga-pg/tables/public_user_quest.yaml @@ -1,3 +1,11 @@ table: name: user_quest schema: public +array_relationships: + - name: user_quest_rewards + using: + foreign_key_constraint_on: + column: user_quest_id + table: + name: user_quest_reward + schema: public diff --git a/hasura/metadata/databases/punkga-pg/tables/public_user_quest_reward.yaml b/hasura/metadata/databases/punkga-pg/tables/public_user_quest_reward.yaml index 45689584..50596639 100644 --- a/hasura/metadata/databases/punkga-pg/tables/public_user_quest_reward.yaml +++ b/hasura/metadata/databases/punkga-pg/tables/public_user_quest_reward.yaml @@ -1,3 +1,7 @@ table: name: user_quest_reward schema: public +object_relationships: + - name: user_quest + using: + foreign_key_constraint_on: user_quest_id diff --git a/src/quest/dto/get-campaign-quest.dto.ts b/src/quest/dto/get-campaign-quest.dto.ts new file mode 100644 index 00000000..f487e3e0 --- /dev/null +++ b/src/quest/dto/get-campaign-quest.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; + +export class GetCampaignQuestParamDto { + @ApiProperty() + quest_id: number; +} + +export class GetCampaignQuestRequestDto { + @ApiPropertyOptional() + user_id: string; +} diff --git a/src/quest/quest.controller.ts b/src/quest/quest.controller.ts index 35992402..daf1151a 100644 --- a/src/quest/quest.controller.ts +++ b/src/quest/quest.controller.ts @@ -2,6 +2,7 @@ import { Body, Controller, Get, + Param, Post, Query, UploadedFiles, @@ -18,6 +19,10 @@ import { RolesGuard } from '../auth/role.guard'; import { UploadNftImageRequestDto } from './dto/upload-nft-image.dto'; import { QuestService } from './quest.service'; import { GetAllCampaignQuestRequestDto } from './dto/get-all-campaign-quest.dto'; +import { + GetCampaignQuestParamDto, + GetCampaignQuestRequestDto, +} from './dto/get-campaign-quest.dto'; @Controller('quest') @ApiTags('quest') @@ -26,8 +31,15 @@ export class QuestController { @Get() getAllCampaignQuest(@Query() query: GetAllCampaignQuestRequestDto) { - const userId = query.user_id; - return this.questSvc.getAllCampaignQuest(userId); + return this.questSvc.getAllCampaignQuest(query.user_id); + } + + @Get(':quest_id') + getCampaignQuestDetail( + @Param() param: GetCampaignQuestParamDto, + @Query() query: GetCampaignQuestRequestDto + ) { + return this.questSvc.get(param.quest_id, query.user_id); } @UseGuards(AuthGuard, RolesGuard) diff --git a/src/quest/quest.module.ts b/src/quest/quest.module.ts index 2eabf9fe..fab346f8 100644 --- a/src/quest/quest.module.ts +++ b/src/quest/quest.module.ts @@ -6,9 +6,22 @@ import { JwtModule } from '@nestjs/jwt'; import { QuestGraphql } from './quest.graphql'; import { GraphqlModule } from '../graphql/graphql.module'; import { UserModule } from '../user/user.module'; +import { RepeatQuestModule } from '../repeat-quests/repeat-quests.module'; +import { SocialActivitiesModule } from '../social-activites/social-activities.module'; +import { UserQuestModule } from '../user-quests/user-quests.module'; +import { SubscribersModule } from '../subscribers/subscribers.module'; @Module({ - imports: [FilesModule, JwtModule, GraphqlModule, UserModule], + imports: [ + FilesModule, + JwtModule, + GraphqlModule, + UserModule, + RepeatQuestModule, + SocialActivitiesModule, + UserQuestModule, + SubscribersModule, + ], providers: [QuestService, QuestGraphql], controllers: [QuestController], exports: [], diff --git a/src/quest/quest.service.ts b/src/quest/quest.service.ts index 9c3f4c62..df201a2c 100644 --- a/src/quest/quest.service.ts +++ b/src/quest/quest.service.ts @@ -3,6 +3,10 @@ import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { FilesService } from '../files/files.service'; import { QuestGraphql } from './quest.graphql'; import { UserGraphql } from '../user/user.graphql'; +import { SocialActivitiesGraphql } from '../social-activites/social-activities.graphql'; +import { SubscribersGraphql } from '../subscribers/subscribers.graphql'; +import { UserQuestsGraphql } from '../user-quests/user-quests.graphql'; +import { RepeatQuestsGraphql } from '../repeat-quests/repeat-quests.graphql'; @Injectable() export class QuestService { @@ -11,7 +15,11 @@ export class QuestService { constructor( private filesService: FilesService, private questGraphql: QuestGraphql, - private userGraphql: UserGraphql + private socialActivitiesGraphql: SocialActivitiesGraphql, + private subscribersGraphql: SubscribersGraphql, + private userGraphql: UserGraphql, + private userQuestGraphql: UserQuestsGraphql, + private repeatQuestGraphql: RepeatQuestsGraphql ) {} async get(questId: number, userId?: string) { @@ -23,19 +31,75 @@ export class QuestService { let reward_status = 0; if (userId) { - reward_status = await this.checkRewardStatus(quest.requirement, userId); + const isClaimed = await this.isClaimed(quest, userId); + if (isClaimed) { + reward_status = 2; + } else { + const canClaimReward = await this.canClaimReward( + quest.requirement, + userId + ); + + if (canClaimReward) reward_status = 1; + } } quest.reward_status = reward_status; return quest; } + async isClaimed(quest: any, userId: string): Promise { + let queryUserQuestCondition; + // check reward claimed + if (quest.type === 'Once') { + queryUserQuestCondition = { + where: { + quest_id: { + _eq: quest.id, + }, + user_id: { + _eq: userId, + }, + }, + }; + } else { + // get latest repeat quest by quest id + const repeatQuest = await this.repeatQuestGraphql.queryRepeatQuest({ + quest_id: quest.id, + }); + + queryUserQuestCondition = { + where: { + repeat_quest_id: { + _eq: repeatQuest.id, + }, + user_id: { + _eq: userId, + }, + }, + }; + } + + // query user quest + const userQuest = await this.userQuestGraphql.queryUserQuests( + queryUserQuestCondition + ); + + if ( + userQuest?.user_quest_rewards && + userQuest?.user_quest_rewards !== null + ) { + return true; + } + return false; + } + /** Reward status * 0: Can not claim reward * 1: Can claim reward * TODO: 2: Claimed */ - async checkRewardStatus(requirement: any, userId: string) { + async canClaimReward(requirement: any, userId: string) { const requirementType = Object.keys(requirement); if (requirementType.includes('read')) { @@ -43,14 +107,26 @@ export class QuestService { } if (requirementType.includes('comment')) { - // do something const chapterId = requirement.comment.chapter.id; + const result = await this.socialActivitiesGraphql.queryActivities({ + chapter_id: chapterId, + user_id: userId, + }); + + if (result.data.social_activities[0]) return true; } if (requirementType.includes('subscribe')) { // do something + const mangaId = requirement.subscribe.manga.id; + const result = await this.subscribersGraphql.querySubscribers({ + manga_id: mangaId, + user_id: userId, + }); + if (result.data.subscribers[0]) return true; } - return 0; + + return false; } async upload(file: Express.Multer.File) { diff --git a/src/repeat-quests/repeat-quests.graphql.ts b/src/repeat-quests/repeat-quests.graphql.ts new file mode 100644 index 00000000..152deaac --- /dev/null +++ b/src/repeat-quests/repeat-quests.graphql.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { GraphqlService } from '../graphql/graphql.service'; + +@Injectable() +export class RepeatQuestsGraphql { + constructor( + private configSvc: ConfigService, + private graphqlSvc: GraphqlService + ) {} + + async queryRepeatQuest(variables: any) { + const result = await this.graphqlSvc.query( + this.configSvc.get('graphql.endpoint'), + '', + `query repeat_quests($quest_id: Int!) { + repeat_quests(where: {quest_id: {_eq: $quest_id}}, order_by: {created_at: desc}, limit: 1) { + id + quest_id + created_at + } + }`, + 'repeat_quests', + variables + ); + + return result.data.repeat_quests[0]; + } +} diff --git a/src/repeat-quests/repeat-quests.module.ts b/src/repeat-quests/repeat-quests.module.ts new file mode 100644 index 00000000..1d043a47 --- /dev/null +++ b/src/repeat-quests/repeat-quests.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; + +import { GraphqlModule } from '../graphql/graphql.module'; +import { RepeatQuestsGraphql } from './repeat-quests.graphql'; + +@Module({ + imports: [GraphqlModule], + providers: [RepeatQuestsGraphql], + exports: [RepeatQuestsGraphql], +}) +export class RepeatQuestModule {} diff --git a/src/social-activites/social-activities.graphql.ts b/src/social-activites/social-activities.graphql.ts new file mode 100644 index 00000000..692b5227 --- /dev/null +++ b/src/social-activites/social-activities.graphql.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { GraphqlService } from '../graphql/graphql.service'; + +@Injectable() +export class SocialActivitiesGraphql { + constructor( + private configSvc: ConfigService, + private graphqlSvc: GraphqlService + ) {} + + queryActivities(variables: any) { + return this.graphqlSvc.query( + this.configSvc.get('graphql.endpoint'), + '', + `query social_activities($chapter_id: Int!, $user_id: bpchar!) { + social_activities(where: {_and: [{chapter_id: {_eq: $chapter_id}}, {user_id: {_eq: $user_id}}]}) { + id + content + user_id + chapter_id + } + } + `, + 'social_activities', + variables + ); + } +} diff --git a/src/social-activites/social-activities.module.ts b/src/social-activites/social-activities.module.ts new file mode 100644 index 00000000..7a47fcd6 --- /dev/null +++ b/src/social-activites/social-activities.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; + +import { GraphqlModule } from '../graphql/graphql.module'; +import { SocialActivitiesGraphql } from './social-activities.graphql'; + +@Module({ + imports: [GraphqlModule], + providers: [SocialActivitiesGraphql], + exports: [SocialActivitiesGraphql], +}) +export class SocialActivitiesModule {} diff --git a/src/subscribers/subscribers.graphql.ts b/src/subscribers/subscribers.graphql.ts new file mode 100644 index 00000000..de152d36 --- /dev/null +++ b/src/subscribers/subscribers.graphql.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { GraphqlService } from '../graphql/graphql.service'; + +@Injectable() +export class SubscribersGraphql { + constructor( + private configSvc: ConfigService, + private graphqlSvc: GraphqlService + ) {} + + querySubscribers(variables: any) { + return this.graphqlSvc.query( + this.configSvc.get('graphql.endpoint'), + '', + `query subscribers($user_id: bpchar!, $manga_id: Int!) { + subscribers(where: {manga_id: {_eq: $manga_id}, user_id: {_eq: $user_id}}) { + id + manga_id + created_at + user_id + } + } + `, + 'subscribers', + variables + ); + } +} diff --git a/src/subscribers/subscribers.module.ts b/src/subscribers/subscribers.module.ts new file mode 100644 index 00000000..0f756c28 --- /dev/null +++ b/src/subscribers/subscribers.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; + +import { GraphqlModule } from '../graphql/graphql.module'; +import { SubscribersGraphql } from './subscribers.graphql'; + +@Module({ + imports: [GraphqlModule], + providers: [SubscribersGraphql], + exports: [SubscribersGraphql], +}) +export class SubscribersModule {} diff --git a/src/user-quests/user-quests.graphql.ts b/src/user-quests/user-quests.graphql.ts new file mode 100644 index 00000000..0e77d066 --- /dev/null +++ b/src/user-quests/user-quests.graphql.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { GraphqlService } from '../graphql/graphql.service'; + +@Injectable() +export class UserQuestsGraphql { + constructor( + private configSvc: ConfigService, + private graphqlSvc: GraphqlService + ) {} + + async queryUserQuests(variables: any) { + const result = await this.graphqlSvc.query( + this.configSvc.get('graphql.endpoint'), + '', + `query user_quest($where: user_quest_bool_exp = {_and: {}}) { + user_quest(where: $where) { + id + user_quest_rewards { + tx_hash + id + created_at + } + } + }`, + 'user_quest', + variables + ); + + return result.data.user_quest[0]; + } +} diff --git a/src/user-quests/user-quests.module.ts b/src/user-quests/user-quests.module.ts new file mode 100644 index 00000000..de8b63e2 --- /dev/null +++ b/src/user-quests/user-quests.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; + +import { GraphqlModule } from '../graphql/graphql.module'; +import { UserQuestsGraphql } from './user-quests.graphql'; + +@Module({ + imports: [GraphqlModule], + providers: [UserQuestsGraphql], + exports: [UserQuestsGraphql], +}) +export class UserQuestModule {}