From e99495c3908a5a0f4dcaa61defaf5c2cf0d1ab60 Mon Sep 17 00:00:00 2001 From: kjk04021 Date: Mon, 16 Jan 2023 18:42:24 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20feat(api):=20add=20api=20for=20?= =?UTF-8?q?group=20post?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/routes/v1/group.ts | 75 +++++++++++++++++++++ src/api/routes/v1/index.ts | 2 + src/api/routes/v1/individual.ts | 4 -- src/interfaces/GroupPost.ts | 28 ++++++++ src/loaders/index.ts | 7 +- src/services/group.ts | 116 ++++++++++++++++++++++++++++++++ src/services/individual.ts | 1 - 7 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 src/api/routes/v1/group.ts create mode 100644 src/interfaces/GroupPost.ts create mode 100644 src/services/group.ts diff --git a/src/api/routes/v1/group.ts b/src/api/routes/v1/group.ts new file mode 100644 index 0000000..b36f77b --- /dev/null +++ b/src/api/routes/v1/group.ts @@ -0,0 +1,75 @@ +// TODO: isLoggedIn 미들웨어 설정 +import asyncHandler from "@/api/middlewares/asyncHandler"; +import { + GroupPostDTO, + GroupPostInputDTO +} from "@/interfaces/GroupPost"; + +import GroupService from "@/services/group"; +import { Router, Request, Response} from "express"; +import { Container } from "typedi"; +import logger from "winston"; + +const route = Router(); + +export default (app: Router) => { + app.use("/groups", route); + + // 개인 롤링페이퍼 포스트 생성 + route.post( + "/posts", + asyncHandler(async (req: Request, res: Response) => { + logger.debug(req.body); + const groupServiceInstance = Container.get(GroupService); + const { groupPost } = await groupServiceInstance.createGroupPost( + req.body as GroupPostDTO, + ); + + return res.status(201).json({ result: groupPost }); + }), + ); + + // 그룹 롤링페이퍼 포스트 디테일 조회 + route.get( + "/posts/:groupPostId", + + asyncHandler(async (req: Request, res: Response) => { + logger.debug(req.params); + const groupServiceInstance = Container.get(GroupService); + const { groupPostDetail} = await groupServiceInstance.getDetailGroupPost( + req.params as GroupPostInputDTO, + ); + return res.status(200).json({ result: groupPostDetail}); + }), + ); + + // 그룹 포스트 수정 + route.put( + "/posts", + asyncHandler(async (req: Request, res: Response) => { + logger.debug(req.body); + const groupServiceInstance = Container.get(GroupService); + const { groupUadatedPostResult} = await groupServiceInstance.updateGroupPost( + req.body as GroupPostInputDTO, + req.body as GroupPostDTO, + ); + // ! 수정한 후 디테일 뷰로 이동! + // 수정한 포스트 결과 + return res.status(201).json({ result: groupUadatedPostResult }); + }), + ); + + // 그룹 포스트 삭제 + route.delete( + "/posts", + asyncHandler(async (req: Request, res: Response) => { + logger.debug(req.body); + const groupServiceInstance = Container.get(GroupService); + const { groupDeletedPostResult } = await groupServiceInstance.deleteGroupPost( + req.body as GroupPostInputDTO, + ); + // 삭제한 포스트 결과 + return res.status(200).json({ result: groupDeletedPostResult }); + }), + ); +}; diff --git a/src/api/routes/v1/index.ts b/src/api/routes/v1/index.ts index d314db0..1198ea1 100644 --- a/src/api/routes/v1/index.ts +++ b/src/api/routes/v1/index.ts @@ -2,6 +2,7 @@ import { Router } from "express"; import auth from "./auth"; import individual from "./individual"; +import group from "./group"; /* * API Version: v1 @@ -15,6 +16,7 @@ export default () => { auth(app); individual(app); + group(app); return app; }; diff --git a/src/api/routes/v1/individual.ts b/src/api/routes/v1/individual.ts index e03f93e..90ce913 100644 --- a/src/api/routes/v1/individual.ts +++ b/src/api/routes/v1/individual.ts @@ -10,10 +10,6 @@ import { } from "@/interfaces/PersonalRollingPaper"; import { UserInputDTO } from "@/interfaces/User"; -// ? routes에선 model 안쓰이는데 뺄까요?? -import PersonalPost from "@/models/personalPost"; -import PersonalRollingPaper from "@/models/personalRollingPaper"; - import PersonalService from "@/services/individual"; import { Router, Request, Response, NextFunction } from "express"; import { Container } from "typedi"; diff --git a/src/interfaces/GroupPost.ts b/src/interfaces/GroupPost.ts new file mode 100644 index 0000000..ccc0b09 --- /dev/null +++ b/src/interfaces/GroupPost.ts @@ -0,0 +1,28 @@ +enum AnonymousType { + 친구 = "친구", + 동료 = "동료", + 애인 = "애인", + 가족 = "가족", +} + +export interface GroupPost { + group_post_id: string; + group_rolling_paper_id: string; + user_id: string | null; + content: string | null; + post_color: string; + anonymous_type: AnonymousType | null; + emoji_type: string | null; +} + +export interface GroupPostInputDTO { + groupPostId: string; +} + +export interface GroupPostDTO { + group_rolling_paper_id: string; + user_id: string | null; + content: string | null; + anonymous_type: AnonymousType | null; + post_color: string; +} diff --git a/src/loaders/index.ts b/src/loaders/index.ts index b96a10e..43d8bf1 100644 --- a/src/loaders/index.ts +++ b/src/loaders/index.ts @@ -25,6 +25,11 @@ export default async ({ expressApp }) => { model: PersonalPost, }; + const groupPostModel = { + name: "groupPostModel", + model: GroupPost, + }; + const personalRollingPaperModel = { name: "personalRollingPaperModel", model: PersonalRollingPaper, @@ -32,7 +37,7 @@ export default async ({ expressApp }) => { // 위에서 생성한 변수를 배열 안에 대입 await dependencyInjectorLoader({ - models: [userModel, personalPostModel, personalRollingPaperModel], + models: [userModel, personalPostModel, personalRollingPaperModel, groupPostModel], }); Logger.info("🏗️ Dependency Injector loaded"); diff --git a/src/services/group.ts b/src/services/group.ts new file mode 100644 index 0000000..61c4588 --- /dev/null +++ b/src/services/group.ts @@ -0,0 +1,116 @@ +import { Model } from "sequelize-typescript"; +import { Service, Inject } from "typedi"; + +import config from "@/config/config"; + +import { + GroupPost, + GroupPostInputDTO, + GroupPostDTO, +} from "@/interfaces/GroupPost"; +import { group } from "console"; + + +@Service() +export default class GroupService { + constructor( + @Inject("groupPostModel") private groupPostModel, + @Inject("logger") private logger, + ) {} + + + + // ! 페이퍼 주인은 안돼, 그룹원인지는 해당 그룹롤링페이퍼에 들어왔을 때 확인 + // 포스트 생성해주는 함수 + public async createGroupPost( + groupPostDTO: GroupPostDTO, + ): Promise<{ groupPost: GroupPost }> { + try { + const groupPost = await this.groupPostModel.create(groupPostDTO); + + if (!groupPost) { + throw new Error("Unable to create post"); + } + + return { groupPost }; + } catch (error) { + this.logger.error(error); + throw error; + } + } + + // 포스트 아이디(UUID)를 DB에서 찾은 후 JSON으로 전달해주는 함수 + public async getDetailGroupPost( + groupPostInputDTO: GroupPostInputDTO, + ): Promise<{ groupPostDetail : GroupPost }> { + try { + const groupPostId = groupPostInputDTO.groupPostId; + const groupPostDetail = await this.groupPostModel.findOne({ + where: { group_post_id: groupPostId }, + }); + + if (!groupPostDetail) { + throw new Error("Post does not exist"); + } + + return { groupPostDetail}; + } catch (error) { + this.logger.error(error); + throw error; + } + } + + // 롤링페이퍼 작성자만 가능 + // 포스트 수정 함수 + public async updateGroupPost( + groupPostInputDTO: GroupPostInputDTO, + groupPostDTO: GroupPostDTO, + ): Promise<{ groupUadatedPostResult: GroupPost }> { + try { + const groupPostId = groupPostInputDTO.groupPostId; + + // ! 수정한 사람 누구인지 확인 로직 필요, 비로그인 유저면 비밀번호 확인해줘야해! + // 이건 로그인 회원 + const groupUadatedPostResult= await this.groupPostModel.update( + { + content:groupPostDTO.content, + post_color:groupPostDTO.post_color, + anonymous_type: groupPostDTO.anonymous_type, + }, + { where: { group_post_id: groupPostId } }, + ); + // 비로그인 회원은 필요한 거- 비밀번호 확인 + + if (!groupUadatedPostResult) { + throw new Error("Unable to update post"); + } + + return { groupUadatedPostResult }; + } catch (error) { + this.logger.error(error); + throw error; + } + } + + // ! 삭제- 유저, 작성자 확인 + public async deleteGroupPost( + groupPostInputDTO: GroupPostInputDTO, + ): Promise<{groupDeletedPostResult: GroupPost }> { + try { + const groupPostId = groupPostInputDTO.groupPostId; + + const groupDeletedPostResult= await this.groupPostModel.destroy({ + where: { group_post_id: groupPostId}, + }); + + if (!groupDeletedPostResult) { + throw new Error("Unable to delete post"); + } + + return { groupDeletedPostResult }; + } catch (error) { + this.logger.error(error); + throw error; + } + } +} diff --git a/src/services/individual.ts b/src/services/individual.ts index 9ae4342..1b83759 100644 --- a/src/services/individual.ts +++ b/src/services/individual.ts @@ -1,4 +1,3 @@ -import { Model } from "sequelize-typescript"; import { Service, Inject } from "typedi"; import config from "@/config/config"; From 5cbe4153bb7c60c7ef517dae3016475ab1e4f3ee Mon Sep 17 00:00:00 2001 From: kjk04021 Date: Mon, 23 Jan 2023 14:55:32 +0900 Subject: [PATCH 2/4] 2-add-group-updateEmojiAndPost --- src/api/routes/v1/group.ts | 34 +++++++++++++++++++++--- src/interfaces/Group.ts | 34 ++++++++++++++++++++++++ src/interfaces/GroupPost.ts | 24 +++++++++-------- src/interfaces/GroupRollingPaper.ts | 13 ++++++++++ src/interfaces/PersonalPost.ts | 24 ++++++++--------- src/services/group.ts | 40 ++++++++++++++++++++++++----- 6 files changed, 137 insertions(+), 32 deletions(-) create mode 100644 src/interfaces/Group.ts create mode 100644 src/interfaces/GroupRollingPaper.ts diff --git a/src/api/routes/v1/group.ts b/src/api/routes/v1/group.ts index b36f77b..c1c50a6 100644 --- a/src/api/routes/v1/group.ts +++ b/src/api/routes/v1/group.ts @@ -2,7 +2,8 @@ import asyncHandler from "@/api/middlewares/asyncHandler"; import { GroupPostDTO, - GroupPostInputDTO + GroupPostInputDTO, + GroupPostEmojiDTO, } from "@/interfaces/GroupPost"; import GroupService from "@/services/group"; @@ -49,13 +50,16 @@ export default (app: Router) => { asyncHandler(async (req: Request, res: Response) => { logger.debug(req.body); const groupServiceInstance = Container.get(GroupService); - const { groupUadatedPostResult} = await groupServiceInstance.updateGroupPost( + const { groupUpdatedPostResult} = await groupServiceInstance.updateGroupPost( req.body as GroupPostInputDTO, req.body as GroupPostDTO, ); - // ! 수정한 후 디테일 뷰로 이동! + // 포스트 수정을 성공했다면 디테일 뷰 + const { groupPostDetail} = await groupServiceInstance.getDetailGroupPost( + req.body as GroupPostInputDTO, + ); // 수정한 포스트 결과 - return res.status(201).json({ result: groupUadatedPostResult }); + return res.status(201).json({ result: groupUpdatedPostResult, post: groupPostDetail}); }), ); @@ -72,4 +76,26 @@ export default (app: Router) => { return res.status(200).json({ result: groupDeletedPostResult }); }), ); + + // 그룹 포스트 이모지 수정 + route.put( + "/posts/emoji", + asyncHandler(async (req: Request, res: Response) => { + logger.debug(req.body); + const groupServiceInstance = Container.get(GroupService); + const { groupUpdatedEmojiResult} = await groupServiceInstance.updateEmojiForPost( + req.body as GroupPostInputDTO, + req.body as GroupPostEmojiDTO, + ); + + // 이모지 수정을 성공했다면 디테일 뷰 + const { groupPostDetail} = await groupServiceInstance.getDetailGroupPost( + req.body as GroupPostInputDTO, + ); + + // ! 수정한 후 디테일 뷰로 이동! + // 수정한 포스트 결과 + return res.status(201).json({ result: groupUpdatedEmojiResult , post: groupPostDetail}); + }), + ); }; diff --git a/src/interfaces/Group.ts b/src/interfaces/Group.ts new file mode 100644 index 0000000..39af929 --- /dev/null +++ b/src/interfaces/Group.ts @@ -0,0 +1,34 @@ + +export interface Group { + groupId: string; + userId: string; + groupName: string; + inviteUrl: string; + +} + +export interface GroupMember{ + groupMemberId: string; + groupId: string; + userId: string; + groupRollingPaperId: string; +} +export interface GroupInputDTO{ + groupId: string; +} + +export interface GroupMemberInputDTO{ + groupMemberId: string; +} + +export interface GroupDTO { + userId: string; + groupName: string; + inviteUrl: string; +} + +export interface GroupMemberDTO{ + groupId: string; + userId: string; + groupRollingPaperId: string; +} \ No newline at end of file diff --git a/src/interfaces/GroupPost.ts b/src/interfaces/GroupPost.ts index ccc0b09..8dc300e 100644 --- a/src/interfaces/GroupPost.ts +++ b/src/interfaces/GroupPost.ts @@ -6,13 +6,13 @@ enum AnonymousType { } export interface GroupPost { - group_post_id: string; - group_rolling_paper_id: string; - user_id: string | null; + groupPostId: string; + groupRollingPaperId: string; + userId: string | null; content: string | null; - post_color: string; - anonymous_type: AnonymousType | null; - emoji_type: string | null; + postColor: string; + anonymousType: AnonymousType | null; + emojiType: string | null; } export interface GroupPostInputDTO { @@ -20,9 +20,13 @@ export interface GroupPostInputDTO { } export interface GroupPostDTO { - group_rolling_paper_id: string; - user_id: string | null; + groupRollingPaperId: string; + userId: string | null; content: string | null; - anonymous_type: AnonymousType | null; - post_color: string; + anonymousType: AnonymousType | null; + postColor: string; +} + +export interface GroupPostEmojiDTO { + emojiType: string | null; } diff --git a/src/interfaces/GroupRollingPaper.ts b/src/interfaces/GroupRollingPaper.ts new file mode 100644 index 0000000..700c762 --- /dev/null +++ b/src/interfaces/GroupRollingPaper.ts @@ -0,0 +1,13 @@ +export interface GroupRollingPaper { + groupRollingPaperId: string; + title: string | null; +} + + +export interface GroupRollingPaperInputDTO { + groupRollingPaperId: string; +} + +export interface GroupRollingPaperDTO { + title: string | null; +} diff --git a/src/interfaces/PersonalPost.ts b/src/interfaces/PersonalPost.ts index e079fc6..fe315b7 100644 --- a/src/interfaces/PersonalPost.ts +++ b/src/interfaces/PersonalPost.ts @@ -6,14 +6,14 @@ enum AnonymousType { } export interface PersonalPost { - personal_post_id: string; - personal_rolling_paper_id: string; - user_id: string | null; + personalPostId: string; + personalRollingPaperId: string; + userId: string | null; content: string | null; - anonymous_type: AnonymousType | null; - post_color: string; - emoji_type: string | null; - non_member_password: string | null; + anonymousType: AnonymousType | null; + postColor: string; + emojiType: string | null; + nonMemberPassword: string | null; } export interface PersonalPostInputDTO { @@ -21,10 +21,10 @@ export interface PersonalPostInputDTO { } export interface PersonalPostDTO { - personal_rolling_paper_id: string; - user_id: string | null; + personalRollingPaperId: string; + userId: string | null; content: string | null; - anonymous_type: AnonymousType | null; - post_color: string; - non_member_password: string | null; + anonymousType: AnonymousType | null; + postColor: string; + nonMemberPassword: string | null; } diff --git a/src/services/group.ts b/src/services/group.ts index 61c4588..41b58b0 100644 --- a/src/services/group.ts +++ b/src/services/group.ts @@ -7,6 +7,7 @@ import { GroupPost, GroupPostInputDTO, GroupPostDTO, + GroupPostEmojiDTO, } from "@/interfaces/GroupPost"; import { group } from "console"; @@ -65,27 +66,27 @@ export default class GroupService { public async updateGroupPost( groupPostInputDTO: GroupPostInputDTO, groupPostDTO: GroupPostDTO, - ): Promise<{ groupUadatedPostResult: GroupPost }> { + ): Promise<{ groupUpdatedPostResult: GroupPost }> { try { const groupPostId = groupPostInputDTO.groupPostId; // ! 수정한 사람 누구인지 확인 로직 필요, 비로그인 유저면 비밀번호 확인해줘야해! // 이건 로그인 회원 - const groupUadatedPostResult= await this.groupPostModel.update( + const groupUpdatedPostResult= await this.groupPostModel.update( { content:groupPostDTO.content, - post_color:groupPostDTO.post_color, - anonymous_type: groupPostDTO.anonymous_type, + post_color:groupPostDTO.postColor, + anonymous_type: groupPostDTO.anonymousType, }, { where: { group_post_id: groupPostId } }, ); // 비로그인 회원은 필요한 거- 비밀번호 확인 - if (!groupUadatedPostResult) { + if (groupUpdatedPostResult[0]===0) { throw new Error("Unable to update post"); } - return { groupUadatedPostResult }; + return { groupUpdatedPostResult }; } catch (error) { this.logger.error(error); throw error; @@ -113,4 +114,31 @@ export default class GroupService { throw error; } } + +// TODO: 롤링페이퍼 주인만 이모지 수정 가능! + public async updateEmojiForPost( + groupPostInputDTO: GroupPostInputDTO, + groupPostEmojiDTO: GroupPostEmojiDTO, + ): Promise<{ groupUpdatedEmojiResult: GroupPost}> { + try { + const groupPostId = groupPostInputDTO.groupPostId; + + const groupUpdatedEmojiResult= await this.groupPostModel.update( + { + emoji_type : groupPostEmojiDTO.emojiType, + }, + { where: { group_post_id: groupPostId } }, + ); + // 비로그인 회원은 필요한 거- 비밀번호 확인 + console.log(groupUpdatedEmojiResult); + if (groupUpdatedEmojiResult[0]=== 0) { + throw new Error("Unable to update emoji for a post"); + } + + return { groupUpdatedEmojiResult}; + } catch (error) { + this.logger.error(error); + throw error; + } + } } From 05d6ff1cf19b1833d0980501ad72f6eb7500b3ab Mon Sep 17 00:00:00 2001 From: it's U <40010878+j2woo@users.noreply.github.com> Date: Mon, 23 Jan 2023 16:09:24 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20apply=20re?= =?UTF-8?q?quested=20reviews?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/group.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/group.ts b/src/services/group.ts index 41b58b0..95e2081 100644 --- a/src/services/group.ts +++ b/src/services/group.ts @@ -129,8 +129,7 @@ export default class GroupService { }, { where: { group_post_id: groupPostId } }, ); - // 비로그인 회원은 필요한 거- 비밀번호 확인 - console.log(groupUpdatedEmojiResult); + if (groupUpdatedEmojiResult[0]=== 0) { throw new Error("Unable to update emoji for a post"); } From b9730e97ec4a83b522841c905bf9a082a86342a6 Mon Sep 17 00:00:00 2001 From: it's U <40010878+j2woo@users.noreply.github.com> Date: Wed, 25 Jan 2023 09:00:23 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20apply=20re?= =?UTF-8?q?quested=20reviews?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/routes/v1/group.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/v1/group.ts b/src/api/routes/v1/group.ts index c907f56..b74bb0c 100644 --- a/src/api/routes/v1/group.ts +++ b/src/api/routes/v1/group.ts @@ -18,7 +18,7 @@ const route = Router(); export default (app: Router) => { app.use("/groups", route); - // 개인 롤링페이퍼 포스트 생성 + // 롤링페이퍼 포스트 생성 route.post( "/posts", asyncHandler(async (req: Request, res: Response) => {