diff --git a/src/controller/keyword.controller.ts b/src/controller/keyword.controller.ts index 7e3eb82..49a2da6 100644 --- a/src/controller/keyword.controller.ts +++ b/src/controller/keyword.controller.ts @@ -4,7 +4,7 @@ import _ from 'lodash'; import CustomError from '../errors/CustomError'; import { HttpCode } from '../errors/HttpCode'; import { CustomRequest } from '../middleware/requestedInfo'; -import { IKeywordUpdatePayload } from '../model/keyword'; +import { IKeywordDocument, IKeywordUpdatePayload, IKeywordWithImage } from '../model/keyword'; import * as KeywordService from '../service/keyword.service'; import * as MemeService from '../service/meme.service'; import { logger } from '../util/logger'; @@ -39,9 +39,22 @@ const deleteKeyword = async (req: CustomRequest, res: Response, next: NextFuncti const getTopKeywords = async (req: Request, res: Response, next: NextFunction) => { const limit = 6; try { - const topKeywords = await KeywordService.getTopKeywords(limit); - logger.info(`Get top ${limit} keywords: ${JSON.stringify(topKeywords)}`); - return res.json(createSuccessResponse(HttpCode.OK, 'Get Top Keywords', topKeywords)); + const topKeywords: IKeywordDocument[] = await KeywordService.getTopKeywords(limit); + // 키워드에 해당하는 밈 이미지 가져오기 + const promises: Promise[] = topKeywords.map(async (keyword: IKeywordDocument) => { + try { + const topReactionImage: string = await MemeService.getTopReactionImage(keyword); + return { ...keyword, topReactionImage } as IKeywordWithImage; + } catch (error) { + logger.error(`Error retrieving top reaction image for keyword: ${keyword}`, error); + throw new CustomError(`Failed to get top reaction image`, HttpCode.INTERNAL_SERVER_ERROR); + } + }); + + const keywordWithImages: IKeywordWithImage[] = await Promise.all(promises); + + logger.info(`Get top ${limit} keywords: ${JSON.stringify(keywordWithImages)}`); + return res.json(createSuccessResponse(HttpCode.OK, 'Get Top Keywords', keywordWithImages)); } catch (err) { return next(new CustomError(err.message, err.status || HttpCode.INTERNAL_SERVER_ERROR)); } diff --git a/src/controller/meme.controller.ts b/src/controller/meme.controller.ts index 1b6b8e6..68a9ec9 100644 --- a/src/controller/meme.controller.ts +++ b/src/controller/meme.controller.ts @@ -234,10 +234,13 @@ const createMemeWatch = async (req: CustomRequest, res: Response, next: NextFunc return res.json(createSuccessResponse(HttpCode.CREATED, 'Crate Meme Watch', result)); } else if (type == MemeWatchType.RECOMMEND) { - const memeRecommendWatch = await UserService.createMemeRecommendWatch(user, meme); + const [recommendMemeWatchCount, _]: [number, any] = await Promise.all([ + UserService.createMemeRecommendWatch(user, meme), + UserService.updateLastSeenMeme(user, meme), + ]); return res.json( - createSuccessResponse(HttpCode.CREATED, `${type} Meme Watch`, memeRecommendWatch), + createSuccessResponse(HttpCode.CREATED, `${type} Meme Watch`, recommendMemeWatchCount), ); } else { return next(new CustomError(`Invalid 'type' parameter.`, HttpCode.BAD_REQUEST)); diff --git a/src/model/keyword.ts b/src/model/keyword.ts index 1f76f87..f7a9658 100644 --- a/src/model/keyword.ts +++ b/src/model/keyword.ts @@ -10,6 +10,10 @@ export interface IKeywordUpdatePayload { category?: string; } +export interface IKeywordWithImage extends IKeyword { + topReactionImage: string; +} + export interface IKeyword { name: string; category: string; diff --git a/src/model/user.ts b/src/model/user.ts index b596dd9..12bffc2 100644 --- a/src/model/user.ts +++ b/src/model/user.ts @@ -11,6 +11,7 @@ export interface IUserInfos extends IUser { reaction: number; save: number; level: number; + memeRecommendWatchCount: number; } export interface IUserDocument extends Document { diff --git a/src/service/meme.service.ts b/src/service/meme.service.ts index 09db62f..5ad9fe4 100644 --- a/src/service/meme.service.ts +++ b/src/service/meme.service.ts @@ -250,6 +250,18 @@ async function deleteMemeSave(user: IUserDocument, meme: IMemeDocument): Promise throw new CustomError(`Failed delete memeSave(${err.message})`, HttpCode.INTERNAL_SERVER_ERROR); } } +async function getTopReactionImage(keyword: IKeywordDocument): Promise { + try { + const topReactionMeme = await MemeModel.findOne({ keywordIds: keyword._id }).sort({ reaction: -1 }); + + logger.info(`Get top reaction meme - keyword(${keyword.name}), meme(${topReactionMeme._id})`); + return topReactionMeme.image; + }catch (err) { + logger.error(`Failed get top reaction meme`, err.message); + throw new CustomError(`Failed get top reaction meme(${err.message})`, HttpCode.INTERNAL_SERVER_ERROR); + } +} + export { getMeme, @@ -263,4 +275,6 @@ export { deleteKeywordOfMeme, getMemeWithKeywords, searchMemeByKeyword, + getTopReactionImage, }; + diff --git a/src/service/user.service.ts b/src/service/user.service.ts index 1b8defe..19fcad7 100644 --- a/src/service/user.service.ts +++ b/src/service/user.service.ts @@ -11,7 +11,7 @@ import { IMemeRecommendWatchCreatePayload, } from '../model/memeRecommendWatch'; import { logger } from '../util/logger'; -import { startOfWeek, format } from 'date-fns'; +import { startOfWeek } from 'date-fns'; async function getUser(deviceId: string): Promise { try { @@ -47,12 +47,20 @@ async function createUser(deviceId: string): Promise { countInteractionType(InteractionType.SAVE), ]); + const todayWeekStart = startOfWeek(new Date(), { weekStartsOn: 1 }); + const memeRecommendWatchCount = await MemeRecommendWatchModel.countDocuments({ + startDate: todayWeekStart, + deviceId: foundUser.deviceId, + isDeleted: false, + }); + return { ...foundUser.toObject(), watch, reaction, save, share, + memeRecommendWatchCount: memeRecommendWatchCount, level: 1, }; } @@ -153,13 +161,11 @@ async function getSavedMeme(user: IUserDocument): Promise { } } -async function createMemeRecommendWatch( - user: IUserDocument, - meme: IMemeDocument, -): Promise { +async function createMemeRecommendWatch(user: IUserDocument, meme: IMemeDocument): Promise { try { const todayWeekStart = startOfWeek(new Date(), { weekStartsOn: 1 }); const memeRecommendWatch = await MemeRecommendWatchModel.findOne({ + memeId: meme._id, startDate: todayWeekStart, deviceId: user.deviceId, isDeleted: false, @@ -176,7 +182,7 @@ async function createMemeRecommendWatch( { _id: memeRecommendWatch._id }, { $set: updatePayload }, ); - return true; + return updatePayload.memeIds.length; } const createPayload: IMemeRecommendWatchCreatePayload = { @@ -187,7 +193,7 @@ async function createMemeRecommendWatch( await MemeRecommendWatchModel.create(createPayload); - return true; + return 1; } catch (err) { logger.error(`Failed create memeRecommendWatch`, err.message); throw new CustomError(