Skip to content

Commit

Permalink
Feat/retrieve chat list (#57)
Browse files Browse the repository at this point in the history
* 채팅 목록 불러오기 기반 작업

* 채팅 목록 불러오기 기반 작업

* 채팅 목록 불러오기 스웨거 작업

* 채팅 보내기 repository 함수 생성

* 일반 질문 지정 질문 route 분리

* 일반 질문 지정 질문 로직 작성

* 지정 질문 선택시에 선생님 한테 메시지 보내짐

* 일반 질문 숏과외 제안시 채팅 개시

* 일반 질문 숏과외 제안시 채팅 개시

* chatting list 반환할때 roomType 지정

* chatting list 반환할때 roomType 지정

* 학생 입장에서 일반질문 올리고 아무도 offer 없을때도 채팅 목록에서 보이게 수정

* 수업 시간 정하기 api 추가

* 수업 끝나면 question.status 도 finish로 바뀜

* 지정 질문 올릴때 question에 selectedteacherId 저장하도록 수정

* 튜터링 객체 만들때 startedAt, EndAt emptystring으로 해둔거 수정

* /tutoring/info 를 학생이 부를 때 선생님이 과외를 시작하지 않았으면 정보를 돌려주지 않고 success: false 가 됨.

* 일반 질문 학생이 선생님 선택하면 다른 선생님한테 메시지 가도록 수정.

* 선생님이 채팅 조회시 일반 질문 거절 당했을때 "normalreserve"로 분류되던걸 , "normalproposed"로 분류되도록 수정

* 선생님이 채팅 조회시 일반 질문 거절 당했을때 텍스트 메시지를 받도록 변경

* refactor /chatting/list

* refactor /chatting/list

* refactor /chatting/list

* 지정 질문 선생님 아이디 유효성 체크

* 채팅 목록 불러오기 기반 작업

* 채팅 목록 불러오기 기반 작업

* 채팅 목록 불러오기 스웨거 작업

* 채팅 보내기 repository 함수 생성

* 일반 질문 지정 질문 route 분리

* 일반 질문 지정 질문 로직 작성

* 지정 질문 선택시에 선생님 한테 메시지 보내짐

* 일반 질문 숏과외 제안시 채팅 개시

* 일반 질문 숏과외 제안시 채팅 개시

* chatting list 반환할때 roomType 지정

* chatting list 반환할때 roomType 지정

* 학생 입장에서 일반질문 올리고 아무도 offer 없을때도 채팅 목록에서 보이게 수정

* 수업 시간 정하기 api 추가

* 수업 끝나면 question.status 도 finish로 바뀜

* 지정 질문 올릴때 question에 selectedteacherId 저장하도록 수정

* 튜터링 객체 만들때 startedAt, EndAt emptystring으로 해둔거 수정

* /tutoring/info 를 학생이 부를 때 선생님이 과외를 시작하지 않았으면 정보를 돌려주지 않고 success: false 가 됨.

* 일반 질문 학생이 선생님 선택하면 다른 선생님한테 메시지 가도록 수정.

* 선생님이 채팅 조회시 일반 질문 거절 당했을때 "normalreserve"로 분류되던걸 , "normalproposed"로 분류되도록 수정

* 선생님이 채팅 조회시 일반 질문 거절 당했을때 텍스트 메시지를 받도록 변경

* refactor /chatting/list

* refactor /chatting/list

* refactor /chatting/list

* 지정 질문 선생님 아이디 유효성 체크

* chatting.schema.ts 수정 status 삭제
  • Loading branch information
seongyunlee authored and w8385 committed Sep 22, 2023
1 parent 67716b6 commit 7b0fd0a
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 134 deletions.
6 changes: 5 additions & 1 deletion src/chatting/chatting.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ export class ChattingRepository {
* 채팅 객체를 생성 합니다
* 이 함수는 user.participantingChattingRooms 에 추가 해주지 않음
*/
async makeChatRoom(teacherId: string, studentId: string, questionId: string) {
async makeChatRoom(
teacherId: string,
studentId: string,
questionId: string,
): Promise<string> {
const chattingRoomId = uuid();
const chatting: Chatting = {
id: chattingRoomId,
Expand Down
254 changes: 126 additions & 128 deletions src/chatting/chatting.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { QuestionRepository } from '../question/question.repository';
import { Fail, Success } from '../response';
import { User } from '../user/entities/user.interface';
import { UserRepository } from '../user/user.repository';
import { ChattingRepository } from './chatting.repository';
import { UpdateChattingDto } from './dto/update-chatting.dto';
import {
ChatList,
ChatRoom,
ChattingStatus,
NestedChatRoomInfo,
} from './items/chat.list';
import { Injectable } from '@nestjs/common';

@Injectable()
Expand All @@ -17,143 +24,134 @@ export class ChattingService {
try {
const userInfo = await this.userRepository.get(userId);

const chattingRoomIds = userInfo.participatingChattingRooms.map(
(roomId) => {
return { id: roomId };
},
const chatRooms: ChatRoom[] = await Promise.all(
//Join Chatting & Question
userInfo.participatingChattingRooms.map(async (roomId) => {
const roomInfo = await this.chattingRepository.getChatRoomInfo(
roomId,
);
const questionInfo = await this.questionRepository.getInfo(
roomInfo.questionId,
);
return this.makeChatItem({ roomInfo, questionInfo }, userInfo);
}),
);

const userRole = userInfo.role;

const result = {
normalProposed: [],
normalReserved: [],
selectedProposed: [],
selectedReserved: [],
};

const normalProposedGrouping = {};

if (userRole == 'student') {
const pendingQuestions =
await this.questionRepository.getStudentPendingQuestions(userId);
pendingQuestions.map((question) => {
normalProposedGrouping[question.id] = {
teachers: [],
isTeacherRoom: false,
roomImage: question.problem.mainImage,
title: question.problem.description,
subject: question.problem.schoolSubject,
};
});
const chatLists = this.groupChatRoomByState(chatRooms);
if (userInfo.role == 'student') {
chatLists.normalProposed = this.groupNormalProposedForStudent(
chatLists.normalReserved,
);
}

if (chattingRoomIds.length > 0) {
const roomInfos = await this.chattingRepository.getChatRoomsInfo(
chattingRoomIds,
);
const roomInfosWithQuestion = await Promise.all(
roomInfos?.map(async (roomInfo) => {
const questionInfo = await this.questionRepository.getInfo(
roomInfo.questionId,
);
console.log(questionInfo);
const { status, isSelect, selectedTeacherId } = questionInfo;
const { schoolSubject, schoolLevel, description } =
questionInfo.problem;

let chatState: 'pending' | 'reserved' | 'refused' = 'pending';

if (status == 'pending') {
chatState = 'pending';
} else if (status == 'reserved') {
if (userRole == 'student' || selectedTeacherId == userId) {
chatState = 'reserved';
} else {
// 선생님이 api 부른 경우에 거절 당한 경우.
chatState = 'refused';
}
}

const item = {
roomImage: undefined,
id: roomInfo.id,
messages: roomInfo.messages.map((message) => {
const isMyMsg = message.sender == userId;
const { body, ...rest } = message;
return { body: JSON.parse(body), isMyMsg: isMyMsg, ...rest };
}),
opponentId: undefined,
questionState: chatState,
problemImages: questionInfo.problem.mainImage,
isSelect: isSelect,
isTeacherRoom: true,
questionId: roomInfo.questionId,
schoolSubject: schoolSubject,
schoolLevel: schoolLevel,
title: undefined,
description: description,
};

if (userRole == 'student') {
const teacherInfo = await this.userRepository.get(
roomInfo.teacherId,
);
const { profileImage, name } = teacherInfo;
item.roomImage = profileImage;
item.title = name;
item.opponentId = teacherInfo.id;
} else {
const studentInfo = await this.userRepository.get(
roomInfo.studentId,
);
const { profileImage, name } = studentInfo;
item.roomImage = profileImage;
item.title = name;
item.opponentId = studentInfo.id;
}

return item;
}),
);
return new Success('채팅방 목록을 불러왔습니다.', chatLists);
} catch (error) {
return new Fail(error.message);
}
}

groupNormalProposedForStudent(chatRooms: ChatRoom[]): ChatRoom[] {
const result = {};
chatRooms.forEach((chatRoom) => {
if (chatRoom.questionId in result) {
result[chatRoom.questionId].teachers.push(chatRoom);
} else {
const questionRoom: ChatRoom = {
teachers: [chatRoom],
isTeacherRoom: false,
roomImage: chatRoom.problemImage,
title: chatRoom.questionInfo.problem.description,
schoolSubject: chatRoom.schoolSubject,
schoolLevel: chatRoom.schoolLevel,
status: ChattingStatus.pending,
questionId: chatRoom.questionId,
problemImage: chatRoom.problemImage,
isSelect: false,
};
result[chatRoom.questionId] = questionRoom;
}
});
return Object.values(result);
}

roomInfosWithQuestion.forEach((roomInfo) => {
if (roomInfo.isSelect) {
//지정 질문
if (roomInfo.questionState === 'pending') {
result.selectedProposed.push(roomInfo);
} else if (roomInfo.questionState === 'reserved') {
result.selectedReserved.push(roomInfo);
}
} else {
//일반 질문
if (
roomInfo.questionState === 'pending' ||
roomInfo.questionState === 'refused'
) {
if (userRole == 'student') {
//grouping by questionId
if (normalProposedGrouping[roomInfo.questionId]) {
normalProposedGrouping[roomInfo.questionId].teachers.push(
roomInfo,
);
}
} else {
result.normalProposed.push(roomInfo);
}
} else {
result.normalReserved.push(roomInfo);
}
}
});
groupChatRoomByState(chatRooms: ChatRoom[]): ChatList {
const result: ChatList = {
normalProposed: [],
normalReserved: [],
selectedProposed: [],
selectedReserved: [],
};

chatRooms.forEach((chatRoom) => {
if (chatRoom.isSelect) {
if (chatRoom.status == ChattingStatus.pending) {
result.selectedProposed.push(chatRoom);
}
if (chatRoom.status == ChattingStatus.reserved) {
result.selectedReserved.push(chatRoom);
}
} else {
if (chatRoom.status == ChattingStatus.pending) {
result.normalProposed.push(chatRoom);
}
if (chatRoom.status == ChattingStatus.reserved) {
result.normalReserved.push(chatRoom);
}
}
if (Object.keys(normalProposedGrouping).length > 0) {
result.normalProposed = Object.values(normalProposedGrouping);
});
return result;
}

async makeChatItem(nestChatRoom: NestedChatRoomInfo, userInfo: User) {
//DB의 질문 정보와 채팅 정보를 API를 호출 한 사람에 맞게 가공한다.

const { roomInfo, questionInfo } = nestChatRoom;

let status: ChattingStatus;
if (questionInfo.status == 'pending') {
status = ChattingStatus.pending;
} else if (questionInfo.status == 'reserved') {
status =
roomInfo.teacherId == questionInfo.selectedTeacherId
? ChattingStatus.reserved
: ChattingStatus.pending;
}
let roomImage: string;
let opponentInfo: User | undefined;

try {
if (userInfo.role == 'student') {
opponentInfo = await this.userRepository.get(roomInfo.teacherId);
} else {
opponentInfo = await this.userRepository.get(roomInfo.studentId);
}
return new Success('채팅방 목록을 불러왔습니다.', result);
} catch (error) {
return new Fail(error.message);
//유저 정보를 가져오는데 실패한 경우.
}

const chatRoom: ChatRoom = {
id: roomInfo.id,
messages: roomInfo.messages.map((message) => {
const { body, ...rest } = message;
try {
return { body: JSON.parse(body), ...rest };
} catch (e) {
return { body: { text: body }, ...rest, format: 'text' };
}
}),
status: status,
roomImage: roomImage,
questionId: questionInfo.id,
schoolSubject: questionInfo.problem.schoolSubject,
schoolLevel: questionInfo.problem.schoolLevel,
problemImage: questionInfo.problem.mainImage,
isSelect: questionInfo.isSelect,
opponentId: opponentInfo?.id,
isTeacherRoom: true,
questionInfo: questionInfo,
title: opponentInfo?.name,
};
return chatRoom;
}

/*
Expand Down
37 changes: 37 additions & 0 deletions src/chatting/items/chat.list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Question } from '../../question/entities/question.interface';
import { Chatting, Message } from '../entities/chatting.interface';
import { Item } from 'nestjs-dynamoose';

export interface ChatList {
normalProposed: ChatRoom[];
normalReserved: ChatRoom[];
selectedProposed: ChatRoom[];
selectedReserved: ChatRoom[];
}

export interface NestedChatRoomInfo {
roomInfo: Item<Chatting>;
questionInfo: Item<Question>;
}

export enum ChattingStatus {
pending = 'pending',
reserved = 'reserved',
}

export interface ChatRoom {
status: ChattingStatus;
messages?: Message[];
id?: string;
title: string;
roomImage: string;
problemImage?: string;
opponentId?: string;
schoolSubject: string;
schoolLevel: string;
isSelect: boolean;
isTeacherRoom: boolean;
questionInfo?: Question;
teachers?: ChatRoom[];
questionId: string;
}
11 changes: 6 additions & 5 deletions src/question/question.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,13 @@ export class QuestionRepository {
createQuestionDto: CreateSelectedQuestionDto,
problemImages: string[],
): Promise<Question> {
const user: User = await this.userRepository.get(userId);
if (user.role === 'teacher') {
throw new Error('선생님은 질문을 생성할 수 없습니다.');
}

try {
const user: User = await this.userRepository.get(userId);

if (user.role === 'teacher') {
throw new Error('선생님은 질문을 생성할 수 없습니다.');
}

return await this.questionModel.create({
createdAt: new Date().toISOString(),
id: questionId,
Expand Down
7 changes: 7 additions & 0 deletions src/question/question.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ export class QuestionService {
questionId,
createQuestionDto,
);

try {
await this.userRepository.get(teacherId);
} catch (e) {
return new Fail('해당 선생님을 찾을 수 없습니다.');
}

const question: Question =
await this.questionRepository.createSelectedQuestion(
questionId,
Expand Down

0 comments on commit 7b0fd0a

Please sign in to comment.