From 2be59eb7bd75bb5bd5f3823955af751f6b903352 Mon Sep 17 00:00:00 2001 From: chs Date: Fri, 14 Feb 2025 18:53:51 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[SWEP-101]=20=ED=9E=88=EC=8A=A4=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20API=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/history.controller.ts | 9 +++++-- src/db.config.ts | 2 +- src/dtos/history.dto.ts | 15 ++++++++---- src/models/history.model.ts | 12 ++++++++++ src/repositories/history.repositories.ts | 30 ++++++++++++++++-------- src/routers/tsoaRoutes.ts | 4 +++- src/services/history.services.ts | 8 +++---- swagger/swagger.json | 25 ++++++++++++++++++-- 8 files changed, 80 insertions(+), 25 deletions(-) diff --git a/src/controllers/history.controller.ts b/src/controllers/history.controller.ts index fbe0252..fb60672 100644 --- a/src/controllers/history.controller.ts +++ b/src/controllers/history.controller.ts @@ -10,6 +10,7 @@ import { Query, Response, Body, + Path, } from 'tsoa'; import { Request as ExpressRequest } from 'express'; import { BaseError, DataValidationError, ServerError } from '../errors.js'; @@ -26,9 +27,11 @@ export class MostTaggedController extends Controller{ * 사용자의 사진들의 카테고리별로 가장 많은 태그를 조회합니다. * * @summary 인기 태그 조회 API + * @param year 인기태그 년도 + * @param month 인기태그 월 * @returns 인기 태그 */ - @Get('/history/most_tagged/get') + @Get('/history/most_tagged/get/:year/:month') @Tags('History') @SuccessResponse('200', 'OK') @Response( @@ -72,13 +75,15 @@ export class MostTaggedController extends Controller{ ) public async getMostTagged( @Request() req: ExpressRequest, + @Path('year') year: number, + @Path('month') month: number ): Promise> { if(!req.user){ throw new DataValidationError({reason: '유저 정보가 없습니다. 다시 로그인 해주세요.'}); } const userId: bigint = req.user.id; - const result: ResponseFromMostTagToClient[] = await serviceGetMostTagged(userId) + const result: ResponseFromMostTagToClient[] = await serviceGetMostTagged(userId, year, month) .then(r => { return r; }) diff --git a/src/db.config.ts b/src/db.config.ts index ced343f..2e1aaee 100644 --- a/src/db.config.ts +++ b/src/db.config.ts @@ -18,4 +18,4 @@ export const pool = mysql.createPool({ queueLimit: 0, // getConnection에서 오류가 발생하기 전에 Pool에 대기할 요청의 개수 한도 }); -export const prisma = new PrismaClient(); +export const prisma = new PrismaClient({log: ['query']}); diff --git a/src/dtos/history.dto.ts b/src/dtos/history.dto.ts index 8e36d06..d3ec877 100644 --- a/src/dtos/history.dto.ts +++ b/src/dtos/history.dto.ts @@ -1,5 +1,5 @@ import { Award, AwardImage } from '@prisma/client'; -import { ResponseFromMostTag, ResponseFromMostTagToClient, ResponseFromAward, ResponseFromUpdateAward } from '../models/history.model.js'; +import { ResponseFromMostTag, ResponseFromMostTagToClient, ResponseFromAward, ResponseFromUpdateAward, ResponseFromAwardImage } from '../models/history.model.js'; export const responseFromMostTag = ( arr: ResponseFromMostTag[] @@ -55,10 +55,14 @@ export const bodyToUpdateAward = ( }; export const responseFromGetAward = ( - awards: Award[] + awards: ResponseFromAwardImage[] ): ResponseFromAward[] => { - return awards.map((value: Award) => { - const {id, userId, awardMonth, createdAt, updatedAt, status} = value; + return awards.map((value: ResponseFromAwardImage) => { + const {id, userId, awardMonth, createdAt, updatedAt, status, images } = value; + + const ims: {imageId: string}[] = images.map((value: {imageId: bigint}) => { + return {imageId: value.imageId.toString()} + }); return { id: id.toString(), @@ -66,7 +70,8 @@ export const responseFromGetAward = ( awardMonth, createdAt, updatedAt, - status + status, + images: ims }; }); }; \ No newline at end of file diff --git a/src/models/history.model.ts b/src/models/history.model.ts index 25a67b0..c09136d 100644 --- a/src/models/history.model.ts +++ b/src/models/history.model.ts @@ -29,4 +29,16 @@ export interface ResponseFromUpdateAward{ updatedAt: Date | null; status: number; awardId: string; +} + +export interface ResponseFromAwardImage{ + id: bigint, + awardMonth: Date, + createdAt: Date, + updatedAt: Date | null, + status: number, + userId: bigint, + images: { + imageId: bigint + }[] } \ No newline at end of file diff --git a/src/repositories/history.repositories.ts b/src/repositories/history.repositories.ts index 3b21b60..0b39aad 100644 --- a/src/repositories/history.repositories.ts +++ b/src/repositories/history.repositories.ts @@ -1,16 +1,17 @@ -import { ResponseFromMostTag } from '../models/history.model.js'; +import { ResponseFromAwardImage, ResponseFromMostTag } from '../models/history.model.js'; import {prisma} from '../db.config.js'; import { AwardImageError, DuplicateAwardError, NoDataFoundError } from '../errors.js'; import { Award, AwardImage } from '@prisma/client'; -export const getMostTagged = async (userId: bigint): Promise => { - const currentTime: Date = new Date(); - const currentMonth: Date = new Date(currentTime.getFullYear(), currentTime.getMonth(), 1); //이번 달의 시작 - const nextMonth: Date = new Date(currentTime.getFullYear() + ( - currentTime.getMonth() === 1 +export const getMostTagged = async (userId: bigint, year: number, month: number): Promise => { + const currentMonth: Date = new Date(year, month - 1, 1); //이번 달의 시작 + const nextMonth: Date = new Date(year + ( + month === 12 ? 1 : 0 - ),(currentTime.getMonth() + 1) % 12, 1); //다음 달의 시작. 12월이면 하나 올리기 + ), month % 12, 1); //다음 달의 시작. 12월이면 하나 올리기 + + //console.log(currentMonth + ' ' + nextMonth); const userImages = await prisma.image.findMany({ where: { @@ -37,7 +38,8 @@ export const getMostTagged = async (userId: bigint): Promise value.id) - } + }, + status: 1 }, select: { tagId: true @@ -139,12 +141,20 @@ export const updateAwardImage = async (userId: bigint, awardId: bigint, imageId: return result; }; -export const getUserAwards = async (userId: bigint): Promise => { - const userAwards: Award[] = await prisma.award.findMany({ +export const getUserAwards = async (userId: bigint): Promise => { + const userAwards: ResponseFromAwardImage[] = await prisma.award.findMany({ + include: { + images: { + select: { + imageId: true + } + } + }, where: { userId: userId } }); + //console.log(userAwards[0].images); return userAwards; }; \ No newline at end of file diff --git a/src/routers/tsoaRoutes.ts b/src/routers/tsoaRoutes.ts index 81f80ee..9ca1dfe 100644 --- a/src/routers/tsoaRoutes.ts +++ b/src/routers/tsoaRoutes.ts @@ -1010,8 +1010,10 @@ export function RegisterRoutes(app: Router) { // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa const argsMostTaggedController_getMostTagged: Record = { req: {"in":"request","name":"req","required":true,"dataType":"object"}, + year: {"in":"path","name":"year","required":true,"dataType":"double"}, + month: {"in":"path","name":"month","required":true,"dataType":"double"}, }; - app.get('/user/history/most_tagged/get', + app.get('/user/history/most_tagged/get/:year/:month', ...(fetchMiddlewares(MostTaggedController)), ...(fetchMiddlewares(MostTaggedController.prototype.getMostTagged)), diff --git a/src/services/history.services.ts b/src/services/history.services.ts index 7aea476..a68b142 100644 --- a/src/services/history.services.ts +++ b/src/services/history.services.ts @@ -1,12 +1,12 @@ import { Award, AwardImage } from '@prisma/client'; import { responseFromGetAward, responseFromMostTag, responseFromNewAward, responseFromUpdateAward } from '../dtos/history.dto.js'; import { AwardUpdateError, DuplicateAwardError, NoDataFoundError } from '../errors.js'; -import { ResponseFromMostTag, ResponseFromMostTagToClient, ResponseFromAward } from '../models/history.model.js'; +import { ResponseFromMostTag, ResponseFromMostTagToClient, ResponseFromAward, ResponseFromAwardImage } from '../models/history.model.js'; import { getMostTagged, getUserAwards, newUserAward, updateAwardImage } from '../repositories/history.repositories.js'; -export const serviceGetMostTagged = async (userId: bigint): Promise => { - const result: ResponseFromMostTag[] = await getMostTagged(userId); +export const serviceGetMostTagged = async (userId: bigint, year: number, month: number): Promise => { + const result: ResponseFromMostTag[] = await getMostTagged(userId, year, month); if(result === null || result.length === 0){ throw new NoDataFoundError({reason: `유저 ${userId}의 태그를 찾을 수 없습니다.`}); @@ -54,7 +54,7 @@ export const serviceUpdateAward = async (userId: bigint, awardId: bigint, mediaI }; export const serviceGetAward = async (userId: bigint): Promise => { - const result: Award[] | null = await getUserAwards(userId); + const result: ResponseFromAwardImage[] | null = await getUserAwards(userId); if(result === null || result.length === 0){ throw new NoDataFoundError({reason: `${userId} 유저의 어워드가 존재하지 않습니다.`}); diff --git a/swagger/swagger.json b/swagger/swagger.json index ec40fd0..dbb3972 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -2718,7 +2718,7 @@ } } }, - "/user/history/most_tagged/get": { + "/user/history/most_tagged/get/{year}/{month}": { "get": { "operationId": "GetMostTagged", "responses": { @@ -2808,7 +2808,28 @@ "History" ], "security": [], - "parameters": [] + "parameters": [ + { + "description": "인기태그 년도", + "in": "path", + "name": "year", + "required": true, + "schema": { + "format": "double", + "type": "number" + } + }, + { + "description": "인기태그 월", + "in": "path", + "name": "month", + "required": true, + "schema": { + "format": "double", + "type": "number" + } + } + ] } }, "/user/history/award/create": { From baf2366fb7df2c1f084f257360139b3401efb507 Mon Sep 17 00:00:00 2001 From: chs Date: Sat, 15 Feb 2025 01:20:08 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[SWEP-101]=20=EC=96=B4=EC=9B=8C=EB=93=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/history.dto.ts | 4 ++-- src/models/history.model.ts | 3 +++ src/repositories/history.repositories.ts | 8 ++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/dtos/history.dto.ts b/src/dtos/history.dto.ts index d3ec877..d2ed4ed 100644 --- a/src/dtos/history.dto.ts +++ b/src/dtos/history.dto.ts @@ -60,8 +60,8 @@ export const responseFromGetAward = ( return awards.map((value: ResponseFromAwardImage) => { const {id, userId, awardMonth, createdAt, updatedAt, status, images } = value; - const ims: {imageId: string}[] = images.map((value: {imageId: bigint}) => { - return {imageId: value.imageId.toString()} + const ims: {imageId: string}[] = images.map((value: {image: {mediaId: bigint}}) => { + return {imageId: value.image.mediaId.toString()}; }); return { diff --git a/src/models/history.model.ts b/src/models/history.model.ts index c09136d..3352aec 100644 --- a/src/models/history.model.ts +++ b/src/models/history.model.ts @@ -40,5 +40,8 @@ export interface ResponseFromAwardImage{ userId: bigint, images: { imageId: bigint + image: { + mediaId: bigint + } }[] } \ No newline at end of file diff --git a/src/repositories/history.repositories.ts b/src/repositories/history.repositories.ts index 0b39aad..90a5df1 100644 --- a/src/repositories/history.repositories.ts +++ b/src/repositories/history.repositories.ts @@ -145,8 +145,12 @@ export const getUserAwards = async (userId: bigint): Promise Date: Wed, 19 Feb 2025 17:48:50 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[SWEP-106]=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../challenge.location.controller.ts | 13 ++++++- src/repositories/challenge.repositories.ts | 37 +++++++++++++++++++ src/repositories/history.repositories.ts | 2 +- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/controllers/challenge.location.controller.ts b/src/controllers/challenge.location.controller.ts index dcb9f7a..c83cc09 100644 --- a/src/controllers/challenge.location.controller.ts +++ b/src/controllers/challenge.location.controller.ts @@ -12,7 +12,7 @@ import { ResponseFromLocationChallenge, } from '../models/challenge.entities.js'; import {bodyToLocationCreation} from '../dtos/challenge.dtos.js'; -import {BaseError, DataValidationError, ServerError} from '../errors.js'; +import {BaseError, DataValidationError, LocationChallengeCreationError, ServerError} from '../errors.js'; import { Controller, Post, @@ -30,6 +30,7 @@ import { ITsoaSuccessResponse, TsoaSuccessResponse, } from '../models/tsoaResponse.js'; +import { challengeExist } from 'src/repositories/challenge.repositories.js'; @Route('challenge') export class LocationController extends Controller { @@ -240,6 +241,16 @@ export class LocationController extends Controller { timestamp: Date; }[], ): Promise> { + if (!req.user) { + throw new DataValidationError({ + reason: '유저 정보가 없습니다. 다시 로그인 해주세요.', + }); + } + + if(await challengeExist(req.user.id)){ + throw new LocationChallengeCreationError({reason: '이미 챌린지가 존재합니다.'}); + } + if (!body) { throw new DataValidationError({reason: '사진 데이터가 없습니다.'}); } diff --git a/src/repositories/challenge.repositories.ts b/src/repositories/challenge.repositories.ts index 4a7caad..1a921a5 100644 --- a/src/repositories/challenge.repositories.ts +++ b/src/repositories/challenge.repositories.ts @@ -12,6 +12,43 @@ import { } from '../models/challenge.entities.js'; import {Challenge, LocationChallenge} from '@prisma/client'; +export const challengeExist = async (userId: bigint): Promise => { + const currentTime = new Date(); //현재 시간 + const minute: number = currentTime.getMinutes() > 30 ? 30 : 0; + currentTime.setMinutes(minute, 0, 0); //시간 초기화 + const nextTime = new Date(); + nextTime.setMinutes(currentTime.getMinutes() + 30, 0, 0); + + // if(minute === 30){ + // currentTime.setHours(currentTime.getHours() - 1); + // nextTime.setHours(nextTime.getHours() - 1); + // } + + // console.log(currentTime); + // console.log(nextTime); + // console.log(new Date(currentTime.toUTCString())); + // console.log(new Date(nextTime.toUTCString())); + + const isExistChallenge = await prisma.challenge.findFirst({ + where: { + userId: userId, + createdAt: { + lt: new Date(nextTime.toUTCString()), + gte: new Date(currentTime.toUTCString()) + } + } + }); + + console.log(isExistChallenge); + + if(isExistChallenge){ + return true; + } + else{ + return false; + } +}; + export const newLocationChallenge = async ( data: LocationChallengeCreation, ): Promise => { diff --git a/src/repositories/history.repositories.ts b/src/repositories/history.repositories.ts index 90a5df1..5d9998e 100644 --- a/src/repositories/history.repositories.ts +++ b/src/repositories/history.repositories.ts @@ -73,7 +73,7 @@ export const getMostTagged = async (userId: bigint, year: number, month: number) export const newUserAward = async (id: bigint): Promise => { const currentDate: Date = new Date(); - currentDate.setMonth(currentDate.getMonth() + 1); + currentDate.setMonth(currentDate.getMonth()); currentDate.setDate(1); currentDate.setHours(0, 0, 0, 0);