Skip to content

Commit ebb9d80

Browse files
authored
Merge pull request #136 from DevKor-github/refactor/timetable
Refactor/timetable
2 parents b7ab547 + 3d16c2b commit ebb9d80

18 files changed

Lines changed: 202 additions & 65 deletions

src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { APP_FILTER } from '@nestjs/core';
2626
import { UnhandledExceptionFilter } from './common/filter/unhandled-exception.filter';
2727
import { KukeyExceptionFilter } from './common/filter/kukey-exception.filter';
2828
import { BannerModule } from './home/banner/banner.module';
29+
import { CommonCourseModule } from './common-course/common-course.module';
2930

3031
console.log(`.env.${process.env.NODE_ENV}`);
3132

@@ -77,6 +78,7 @@ console.log(`.env.${process.env.NODE_ENV}`);
7778
ReportModule,
7879
AttendanceCheckModule,
7980
BannerModule,
81+
CommonCourseModule,
8082
],
8183
controllers: [AppController],
8284
providers: [
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Module } from '@nestjs/common';
2+
import { CommonCourseService } from './common-course.service';
3+
4+
@Module({
5+
providers: [CommonCourseService],
6+
exports: [CommonCourseService],
7+
})
8+
export class CommonCourseModule {}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { InjectDataSource } from '@nestjs/typeorm';
3+
import { DataSource } from 'typeorm';
4+
5+
@Injectable()
6+
export class CommonCourseService {
7+
constructor(
8+
@InjectDataSource()
9+
private readonly dataSource: DataSource,
10+
) {}
11+
12+
async getCourseReviewCount(
13+
coursePairs: {
14+
courseCode: string;
15+
professorName: string;
16+
}[],
17+
): Promise<Map<string, number>> {
18+
if (coursePairs.length === 0) {
19+
return new Map();
20+
}
21+
22+
const placeholders = coursePairs.map(() => '(?, ?)').join(',');
23+
24+
const params = coursePairs.flatMap((pair) => [
25+
pair.courseCode,
26+
pair.professorName,
27+
]);
28+
29+
const query = `
30+
SELECT courseCode as "courseCode", professorName as "professorName", COUNT(*) as reviewCount
31+
FROM course_review
32+
WHERE (courseCode, professorName) IN (${placeholders})
33+
GROUP BY courseCode, professorName
34+
`;
35+
36+
const reviewCounts = await this.dataSource.query(query, params);
37+
38+
const reviewCountMap = new Map<string, number>();
39+
reviewCounts.forEach((item) => {
40+
const key = `${item.courseCode}-${item.professorName}`;
41+
reviewCountMap.set(key, parseInt(item.reviewCount));
42+
});
43+
44+
return reviewCountMap;
45+
}
46+
}

src/course-review/course-review.controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export class CourseReviewController {
9090
);
9191
}
9292

93+
// TODO: 강의관련이므로 강의평 모듈에서 강의모듈로 옮기기
9394
@Get('course')
9495
async getCoursesWithCourseReviews(
9596
@Query()

src/course-review/course-review.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import { CourseModule } from 'src/course/course.module';
99
import { CourseReviewRecommendEntity } from 'src/entities/course-review-recommend.entity';
1010
import { RecentCourseReviewsStrategy } from './strategy/recent-course-reviews-strategy';
1111
import { GoodTeachingSkillReviewsStrategy } from './strategy/good-teaching-skill-reviews-strategy';
12-
12+
import { CommonCourseModule } from 'src/common-course/common-course.module';
1313
@Module({
1414
imports: [
1515
TypeOrmModule.forFeature([CourseReviewEntity, CourseReviewRecommendEntity]),
1616
AuthModule,
1717
UserModule,
18+
// TODO: CourseModule 의존성 없애기
1819
CourseModule,
20+
CommonCourseModule,
1921
],
2022
controllers: [CourseReviewController],
2123
providers: [

src/course/course.controller.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { CourseDocs } from 'src/decorators/docs/course.decorator';
77
import { SearchCourseNewDto } from './dto/search-course-new.dto';
88
import { GetRecommendationCoursesRequestDto } from './dto/get-recommendation-courses-request.dto';
99
import { CommonCourseResponseDto } from './dto/common-course-response.dto';
10+
import { SearchAllTimeCoursesRequestDto } from './dto/search-all-time-courses-request.dto';
11+
import { SearchAllTimeCoursesResponseDto } from './dto/search-all-time-courses-response.dto';
1012

1113
@ApiTags('course')
1214
@CourseDocs
@@ -24,6 +26,15 @@ export class CourseController {
2426
);
2527
}
2628

29+
@Get('all-time')
30+
async searchAllTimeCourses(
31+
@Query() searchAllTimeCoursesRequestDto: SearchAllTimeCoursesRequestDto,
32+
): Promise<SearchAllTimeCoursesResponseDto> {
33+
return await this.courseService.searchAllTimeCourses(
34+
searchAllTimeCoursesRequestDto,
35+
);
36+
}
37+
2738
@UseGuards(JwtAuthGuard)
2839
@Get()
2940
async searchCourses(

src/course/course.module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import { AcademicFoundationSearchStrategy } from './strategy/academic-foundation
1010
import { GeneralSearchStrategy } from './strategy/general-search-strategy';
1111
import { MajorSearchStrategy } from './strategy/major-search-strategy';
1212
import { AllCoursesSearchStrategy } from './strategy/all-courses-search-strategy';
13+
import { CommonCourseModule } from 'src/common-course/common-course.module';
1314

1415
@Module({
15-
imports: [TypeOrmModule.forFeature([CourseEntity, CourseDetailEntity])],
16+
imports: [
17+
TypeOrmModule.forFeature([CourseEntity, CourseDetailEntity]),
18+
CommonCourseModule,
19+
],
1620
controllers: [CourseController],
1721
providers: [
1822
CourseService,

src/course/course.service.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ import { SearchCourseNewDto } from './dto/search-course-new.dto';
1111
import { CourseSearchStrategy } from './strategy/course-search-strategy';
1212
import { SearchCourseReviewsWithKeywordRequest } from 'src/course-review/dto/search-course-reviews-with-keyword-request.dto';
1313
import { GetRecommendationCoursesRequestDto } from './dto/get-recommendation-courses-request.dto';
14-
14+
import { SearchAllTimeCoursesRequestDto } from './dto/search-all-time-courses-request.dto';
15+
import { SearchAllTimeCoursesResponseDto } from './dto/search-all-time-courses-response.dto';
16+
import { CommonCourseService } from 'src/common-course/common-course.service';
1517
@Injectable()
1618
export class CourseService {
1719
constructor(
1820
private courseRepository: CourseRepository,
1921
private courseDetailRepository: CourseDetailRepository,
2022
@Inject('CourseSearchStrategy')
2123
private readonly strategies: CourseSearchStrategy[],
24+
private readonly commonCourseService: CommonCourseService,
2225
) {}
2326
async getCourse(courseId: number): Promise<CommonCourseResponseDto> {
2427
const course = await this.courseRepository.findOne({
@@ -222,6 +225,53 @@ export class CourseService {
222225
return courses.map((course) => new CommonCourseResponseDto(course));
223226
}
224227

228+
async searchAllTimeCourses(
229+
searchAllTimeCoursesRequestDto: SearchAllTimeCoursesRequestDto,
230+
): Promise<SearchAllTimeCoursesResponseDto> {
231+
const { keyword, cursorId } = searchAllTimeCoursesRequestDto;
232+
let coursesQuery = this.courseRepository
233+
.createQueryBuilder('course')
234+
.where(
235+
'course.courseName LIKE :keyword OR course.professorName LIKE :keyword OR course.courseCode LIKE :keyword',
236+
{
237+
keyword: `%${keyword}%`,
238+
},
239+
);
240+
241+
if (cursorId) {
242+
coursesQuery = coursesQuery.andWhere('course.id > :cursorId', {
243+
cursorId,
244+
});
245+
}
246+
247+
const courses = await coursesQuery
248+
.orderBy('course.id', 'ASC')
249+
.take(PaginatedCoursesDto.LIMIT)
250+
.getMany();
251+
252+
const coursePairs = courses.map((course) => ({
253+
courseCode: course.courseCode.split('-')[0],
254+
professorName: course.professorName,
255+
}));
256+
257+
const reviewCountMap =
258+
await this.commonCourseService.getCourseReviewCount(coursePairs);
259+
260+
const coursesWithReviewCount = courses.map((course) => {
261+
const key = `${course.courseCode.split('-')[0]}-${course.professorName}`;
262+
return {
263+
id: course.id,
264+
professorName: course.professorName,
265+
courseName: course.courseName,
266+
courseCode: course.courseCode,
267+
totalRate: course.totalRate,
268+
reviewCount: reviewCountMap.get(key) || 0,
269+
};
270+
});
271+
272+
return new SearchAllTimeCoursesResponseDto(coursesWithReviewCount);
273+
}
274+
225275
private async findSearchStrategy(
226276
searchCourseNewDto: SearchCourseNewDto,
227277
): Promise<CourseSearchStrategy> {

src/course/dto/get-academic-foundation-course.dto.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/course/dto/get-general-course.dto.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)