Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:Sweepic/sweepic-Server into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
GodUser1005 committed Feb 20, 2025
2 parents 6f3179c + 21595ad commit 732cdd5
Show file tree
Hide file tree
Showing 8 changed files with 353 additions and 4 deletions.
76 changes: 76 additions & 0 deletions src/controllers/challenge.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import {
Get,
Query,
Response,
Post,
} from 'tsoa';
import {
serviceAcceptChallenge,
serviceChallengeImageUpload,
serviceCompleteChallenge,
serviceDeleteChallenge,
serviceGetByUserId,
Expand Down Expand Up @@ -438,4 +440,78 @@ export class ChallengeController extends Controller {

return new TsoaSuccessResponse(result);
}

/**
* 챌린지의 이미지들을 업로드합니다.
*
* @summary 챌린지 이미지 업로드 API
* @param challengeId 챌린지 ID
* @param req
* @param body 이미지ID의 배열(string[])
* @returns 업로드 성공 유무
*/
@Post('/images/upload/:challengeId')
@Tags('Challenge')
@SuccessResponse(StatusCodes.OK, '챌린지 이미지 업로드 성공 응답')
@Response<ITsoaErrorResponse>(StatusCodes.BAD_REQUEST, 'Not Found', {
resultType: 'FAIL',
error: {
errorCode: 'SRH-400',
reason: 'req.user 정보가 없습니다.',
data: null,
},
success: null,
})
@Response<ITsoaErrorResponse>(StatusCodes.BAD_REQUEST, 'Not Found', {
resultType: 'FAIL',
error: {
errorCode: 'CHL-400',
reason: '이미지 업로드 중 문제가 발생했습니다.',
data: null,
},
success: null,
})
@Response<ITsoaErrorResponse>(StatusCodes.NOT_FOUND, 'Not Found', {
resultType: 'FAIL',
error: {
errorCode: 'CHL-404',
reason: '이미지가 서버에 존재하지 않습니다.',
data: null,
},
success: null,
})
@Response<ITsoaErrorResponse>(
StatusCodes.INTERNAL_SERVER_ERROR,
'Internal Server Error',
{
resultType: 'FAIL',
error: {
errorCode: 'SER-001',
reason: '내부 서버 오류입니다.',
data: null,
},
success: null,
},
)
public async handleChallengeImageUpload(
@Path('challengeId') challengeId: string,
@Request() req: ExpressRequest,
@Body() body: string[]
): Promise<ITsoaSuccessResponse<string>>{
if (!req.user) {
throw new DataValidationError({reason: 'req.user 정보가 없습니다.'});
}

await serviceChallengeImageUpload(body, challengeId, req.user.id)
.then(() => {return;})
.catch(err => {
if (!(err instanceof BaseError)) {
throw new ServerError();
} else {
throw err;
}
});

return new TsoaSuccessResponse(`${challengeId}챌린지 이미지 업로드 성공`);
}
}
20 changes: 19 additions & 1 deletion src/dtos/challenge.dtos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,21 @@ export const responseFromGetByUserId = (
): ResponseFromGetByUserIdReform[] => {
return challenges.map((value: ResponseFromGetByUserId) => {
const {id, title, context, requiredCount, remainingCount, userId,
createdAt, updatedAt, acceptedAt, completedAt, status, locationChallenge, dateChallenge
createdAt, updatedAt, acceptedAt, completedAt, status, locationChallenge, dateChallenge, images
} = value;

const imageList: string[] = images.map((value: {image: {mediaId: bigint}}) =>
{
return value.image.mediaId.toString();
});

return {
id: id.toString(),
title,
context,
challengeLocation: locationChallenge?.challengeLocation,
challengeDate: dateChallenge?.challengeDate,
images: imageList,
requiredCount,
remainingCount,
userId: userId.toString(),
Expand Down Expand Up @@ -128,4 +134,16 @@ export const bodyToWeeklyCreation = (data: BodyToWeeklyCreation, userId: bigint)
challengeDate,
required
};
};

export const challengeImageUplaodBody = (imageIdList: {id: bigint}[], challengeId: bigint): {
imageId: bigint,
challengeId: bigint
}[] => {
return imageIdList.map((value: {id: bigint}) => {
return {
imageId: value.id,
challengeId
};
});
};
14 changes: 14 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,20 @@ export class DateChallengeNotFoundError extends BaseError {
}
}

// 챌린지 이미지 업로드 에러
export class ChallengeImageUploadError extends BaseError {
constructor(details: {reason: string}) {
super(400, 'CHL-400', '이미지 업로드 중 문제가 발생했습니다.', details);
}
}

// 챌린지 이미지 존재하지 않음 에러
export class ChallengeImageMissingError extends BaseError {
constructor(details: {reason: string}) {
super(404, 'CHL-404', '이미지가 서버에 존재하지 않습니다.', details);
}
}

// 네이버 API 관련 에러
export class NaverGeoCodeError extends BaseError {
constructor(details: {reason: string}) {
Expand Down
4 changes: 4 additions & 0 deletions src/models/challenge.entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ export interface ResponseFromGetByUserId {
dateChallenge: {
challengeDate: Date;
} | null;
images: {
image: {mediaId: bigint}
}[];

id: bigint;
userId: bigint;
Expand All @@ -111,6 +114,7 @@ export interface ResponseFromGetByUserIdReform {
context: string;
challengeLocation: string | undefined;
challengeDate: Date | undefined;
images: string[];
requiredCount: number;
remainingCount: number;
createdAt: Date;
Expand Down
56 changes: 54 additions & 2 deletions src/repositories/challenge.repositories.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { challengeImageUplaodBody } from 'src/dtos/challenge.dtos.js';
import {prisma} from '../db.config.js';
import {
ChallengeAcceptError,
ChallengeCompleteError,
ChallengeDeletionError,
ChallengeImageMissingError,
ChallengeImageUploadError,
ChallengeUpdateError
} from '../errors.js';
import {
Expand Down Expand Up @@ -39,8 +42,6 @@ export const challengeExist = async (userId: bigint): Promise<Boolean> => {
}
});

console.log(isExistChallenge);

if(isExistChallenge){
return true;
}
Expand Down Expand Up @@ -216,8 +217,59 @@ export const getChallengeByUserId = async (
challengeDate: true,
},
},
images: {
select: {
image: {
select: {
mediaId: true
}
}
}
}
},
});

//console.log(challenges[0].images);

return challenges;
};

export const challengeImageUpload = async (
imageIdList: bigint[],
challengeId: bigint,
userId: bigint
): Promise<{count: number}> => {
const duplicateChallenge = await prisma.challengeImage.findFirst({
where: {
challengeId: challengeId
}
});

if(duplicateChallenge){
throw new ChallengeImageUploadError({reason: `${challengeId}챌린지에 이미지가 이미 존재합니다.`});
}

const foundImage = await prisma.image.findMany({
where: {
mediaId: {
in: imageIdList
},
userId: userId
},
select: {
id: true
}
});

if(foundImage.length !== imageIdList.length){
throw new ChallengeImageMissingError({reason: '서버에 존재하지 않는 이미지가 있습니다.'});
}

const inputData = challengeImageUplaodBody(foundImage, challengeId);

const upload = await prisma.challengeImage.createMany({
data: inputData
});

return upload;
};
33 changes: 33 additions & 0 deletions src/routers/tsoaRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ const models: TsoaRoute.Models = {
"context": {"dataType":"string","required":true},
"challengeLocation": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"undefined"}],"required":true},
"challengeDate": {"dataType":"union","subSchemas":[{"dataType":"datetime"},{"dataType":"undefined"}],"required":true},
"images": {"dataType":"array","array":{"dataType":"string"},"required":true},
"requiredCount": {"dataType":"double","required":true},
"remainingCount": {"dataType":"double","required":true},
"createdAt": {"dataType":"datetime","required":true},
Expand Down Expand Up @@ -1842,6 +1843,38 @@ 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 argsChallengeController_handleChallengeImageUpload: Record<string, TsoaRoute.ParameterSchema> = {
challengeId: {"in":"path","name":"challengeId","required":true,"dataType":"string"},
req: {"in":"request","name":"req","required":true,"dataType":"object"},
body: {"in":"body","name":"body","required":true,"dataType":"array","array":{"dataType":"string"}},
};
app.post('/challenge/images/upload/:challengeId',
...(fetchMiddlewares<RequestHandler>(ChallengeController)),
...(fetchMiddlewares<RequestHandler>(ChallengeController.prototype.handleChallengeImageUpload)),

async function ChallengeController_handleChallengeImageUpload(request: ExRequest, response: ExResponse, next: any) {

// 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

let validatedArgs: any[] = [];
try {
validatedArgs = templateService.getValidatedArgs({ args: argsChallengeController_handleChallengeImageUpload, request, response });

const controller = new ChallengeController();

await templateService.apiHandler({
methodName: 'handleChallengeImageUpload',
controller,
response,
next,
validatedArgs,
successStatus: 200,
});
} catch (err) {
return next(err);
}
});
// 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

// 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

Expand Down
16 changes: 16 additions & 0 deletions src/services/challenge.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
acceptChallenge,
getChallengeByUserId,
completeChallenge,
challengeImageUpload,
} from '../repositories/challenge.repositories.js';
import {
ChallengeUpdateError,
Expand Down Expand Up @@ -107,3 +108,18 @@ export const serviceGetByUserId = async (
throw error;
}
};

export const serviceChallengeImageUpload = async (
imageIdList: string[],
challengeId: string,
userId: bigint
): Promise<void> => {
const imageList: bigint[] = imageIdList.map((value: string) => {return BigInt(value);});
const challenge: bigint = BigInt(challengeId);

const result: {count: number}= await challengeImageUpload(imageList, challenge, userId);

if(imageIdList.length !== result.count){
throw new Error('챌린지에 이미지 업로드 중 문제가 생겼습니다.');
}
};
Loading

0 comments on commit 732cdd5

Please sign in to comment.