Skip to content

Commit

Permalink
[BE#208] 소셜화면 API 작성 (#265)
Browse files Browse the repository at this point in the history
* feat: 소셜화면 진입시 팔로워들에 대한 학습기록 조회

- DB query를 직접 작성하여 Left Join 활용
- image_url 처리부분 함수로 분리

* feat: 특정 유저에 대해서 일주일치 학습 시간

- 기존의 calculateTotalTimes 함수를 재활용
- date 다루기 힘들어서 moment 라이브러리 설치

* refactor: console.log 없앰
  • Loading branch information
yeongbinim authored Nov 29, 2023
1 parent 50ad022 commit 8f75778
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 44 deletions.
19 changes: 19 additions & 0 deletions BE/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions BE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
"@nestjs/serve-static": "^4.0.0",
"@nestjs/swagger": "^7.1.15",
"@nestjs/typeorm": "^10.0.0",
"@types/moment": "^2.13.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"ioredis": "^5.3.2",
"moment": "^2.29.4",
"multer": "^1.4.5-lts.1",
"mysql": "^2.18.1",
"nest-winston": "^1.9.4",
Expand Down
23 changes: 11 additions & 12 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
UseInterceptors,
UploadedFile,
BadRequestException,
Patch,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Response } from 'express';
Expand All @@ -27,8 +28,8 @@ import { UsersService } from 'src/users/users.service';
import { UpdateUserDto } from 'src/users/dto/update-user.dto';
import { FileInterceptor } from '@nestjs/platform-express';
import { ConfigService } from '@nestjs/config';
import * as path from 'path';
import { ENV } from 'src/common/const/env-keys.const';
import { getImageUrl } from 'src/common/utils';

@ApiTags('로그인 페이지')
@Controller('auth')
Expand Down Expand Up @@ -64,7 +65,7 @@ export class AuthController {
res.redirect('/');
}

@Post('info')
@Patch('info')
@UseGuards(AccessTokenGuard)
@UseInterceptors(FileInterceptor('image'))
@ApiOperation({ summary: '유저 정보 설정 (완)' })
Expand All @@ -79,7 +80,6 @@ export class AuthController {
@UploadedFile() file: Express.Multer.File,
): Promise<any> {
let image_url: string;
console.log(file);
if (file) {
const isNomal = await this.usersService.isNormalImage(file);
if (!isNomal) {
Expand All @@ -95,12 +95,10 @@ export class AuthController {
return {
nickname: updatedUser.nickname,
email: updatedUser.email,
image_url: updatedUser.image_url
? path.join(
this.configService.get(ENV.CDN_ENDPOINT),
updatedUser.image_url,
)
: null,
image_url: getImageUrl(
this.configService.get(ENV.CDN_ENDPOINT),
updatedUser.image_url,
),
};
}

Expand All @@ -116,9 +114,10 @@ export class AuthController {
return {
nickname: user.nickname,
email: user.email,
image_url: user.image_url
? path.join(this.configService.get(ENV.CDN_ENDPOINT), user.image_url)
: null,
image_url: getImageUrl(
this.configService.get(ENV.CDN_ENDPOINT),
user.image_url,
),
};
}
}
6 changes: 6 additions & 0 deletions BE/src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as path from 'path';

export function transformDate(date: Date): string {
const year = date.getFullYear();
const month = date.getMonth() + 1;
Expand All @@ -8,3 +10,7 @@ export function transformDate(date: Date): string {

return `${year}-${monthStr}-${dayStr}`;
}

export function getImageUrl(prefix: string, image_url: string): string {
return image_url ? path.join(prefix, image_url) : null;
}
29 changes: 24 additions & 5 deletions BE/src/mates/mates.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import {
Post,
Body,
UseGuards,
Query,
} from '@nestjs/common';
import {
ApiBearerAuth,
ApiBody,
ApiCreatedResponse,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { User } from 'src/users/decorator/user.decorator';
Expand All @@ -31,9 +33,17 @@ export class MatesController {
@ApiCreatedResponse({
description: 'OK',
})
@ApiQuery({
name: 'date',
example: '2023-11-22',
description: '날짜',
})
@ApiOperation({ summary: '모든 친구들 조회하기 (완)' })
getMates(@User('id') user_id: number): Promise<object> {
return this.matesService.getMates(user_id);
getMates(
@User('id') user_id: number,
@Query('date') date: string,
): Promise<object> {
return this.matesService.getMates(user_id, date);
}

@Get('/status')
Expand All @@ -44,12 +54,21 @@ export class MatesController {
return this.matesService.getMatesStatus(user_id);
}

@Get('/:mate_id/stats')
@Get('/:following_id/stats')
@UseGuards(AccessTokenGuard)
@ApiQuery({
name: 'date',
example: '2023-11-22',
description: '날짜',
})
@ApiBearerAuth()
@ApiOperation({ summary: '특정 친구의 통계 조회하기' })
getMateStats(@Param('mate_id') id: string) {
id;
getMateStats(
@User('id') user_id: number,
@Param('following_id') following_id: number,
@Query('date') date: string,
): Promise<object> {
return this.matesService.getMateAndMyStats(user_id, following_id, date);
}

@Post()
Expand Down
8 changes: 1 addition & 7 deletions BE/src/mates/mates.entity.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { Entity, PrimaryGeneratedColumn, ManyToOne, JoinColumn } from 'typeorm';
import { UsersModel } from 'src/users/entity/users.entity';

@Entity()
Expand Down
8 changes: 7 additions & 1 deletion BE/src/mates/mates.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ import { Mates } from './mates.entity';
import { AuthModule } from 'src/auth/auth.module';
import { UsersModule } from 'src/users/users.module';
import { RedisService } from 'src/common/redis.service';
import { StudyLogsModule } from 'src/study-logs/study-logs.module';

@Module({
imports: [TypeOrmModule.forFeature([Mates]), AuthModule, UsersModule],
imports: [
TypeOrmModule.forFeature([Mates]),
AuthModule,
UsersModule,
StudyLogsModule,
],
controllers: [MatesController],
providers: [MatesService, RedisService],
})
Expand Down
62 changes: 57 additions & 5 deletions BE/src/mates/mates.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import { Repository } from 'typeorm';
import { MatesDto } from './dto/response/mates.dto';
import { UsersModel } from 'src/users/entity/users.entity';
import { RedisService } from 'src/common/redis.service';
import { getImageUrl } from 'src/common/utils';
import { ConfigService } from '@nestjs/config';
import { ENV } from 'src/common/const/env-keys.const';
import { StudyLogsService } from 'src/study-logs/study-logs.service';
import * as moment from 'moment';

@Injectable()
export class MatesService {
Expand All @@ -18,17 +23,64 @@ export class MatesService {
@InjectRepository(UsersModel)
private userRepository: Repository<UsersModel>,
private redisService: RedisService,
private configService: ConfigService,
private studyLogsService: StudyLogsService,
) {}

async getMates(user_id: number): Promise<object> {
const result = await this.matesRepository.find({
where: { follower_id: { id: user_id } },
});
async getMateAndMyStats(
user_id: number,
following_id: number,
date: string,
): Promise<object> {
const start_date = moment(date).subtract(6, 'days').format('YYYY-MM-DD');
const my_daily_data = await this.studyLogsService.calculateTotalTimes(
user_id,
start_date,
date,
);
const following_daily_data =
await this.studyLogsService.calculateTotalTimes(
following_id,
start_date,
date,
);
// 랭킹1위 카테고리 조회 로직 - ToDo
return {
following_ids: result.map((mate) => mate.following_id.id),
my_daily_data,
following_daily_data,
following_primary_category: null,
};
}

async getMates(user_id: number, date: string): Promise<object[]> {
const studyTimeByFollowing = await this.userRepository.query(
`
SELECT u.id, u.nickname, u.image_url, COALESCE(SUM(s.learning_time), 0) AS total_time
FROM users_model u
LEFT JOIN mates m ON m.following_id = u.id
LEFT JOIN study_logs s ON s.user_id = u.id AND s.date = ?
WHERE m.follower_id = ?
GROUP BY u.id
ORDER BY total_time DESC
`,
[date, user_id],
);
return Promise.all(
studyTimeByFollowing.map(async (record) => {
const started_at = await this.redisService.get(`${record.id}`);
return {
...record,
image_url: getImageUrl(
this.configService.get(ENV.CDN_ENDPOINT),
record.image_url,
),
total_time: parseInt(record.total_time),
started_at,
};
}),
);
}

async getMatesStatus(user_id: number): Promise<object[]> {
const result = await this.matesRepository.find({
where: { follower_id: { id: user_id } },
Expand Down
1 change: 1 addition & 0 deletions BE/src/study-logs/study-logs.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ import { RedisService } from 'src/common/redis.service';
imports: [TypeOrmModule.forFeature([StudyLogs]), AuthModule, UsersModule],
providers: [StudyLogsService, RedisService],
controllers: [StudyLogsController, StatsController],
exports: [StudyLogsService, TypeOrmModule],
})
export class StudyLogsModule {}
33 changes: 24 additions & 9 deletions BE/src/study-logs/study-logs.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Categories } from 'src/categories/categories.entity';
import { StudyLogsDto } from './dto/response/study-logs.dto';
import { transformDate } from 'src/common/utils';
import { RedisService } from 'src/common/redis.service';
import * as moment from 'moment';

@Injectable()
export class StudyLogsService {
Expand Down Expand Up @@ -56,17 +57,31 @@ export class StudyLogsService {
return started_at;
}

private async calculateTotalTime(
async calculateTotalTimes(
user_id: number,
date: string,
): Promise<number> {
const sum = await this.studyLogsRepository
start_date: string,
end_date: string,
): Promise<number[]> {
const startMoment = moment(start_date);
const diffDays = moment(end_date).diff(startMoment, 'days');
const result = Array.from({ length: diffDays }, () => 0);
const daily_sums = await this.studyLogsRepository
.createQueryBuilder('study_logs')
.select('SUM(study_logs.learning_time)', 'sum')
.select('study_logs.date', 'date')
.addSelect('SUM(study_logs.learning_time)', 'daily_sum')
.where('study_logs.user_id = :user_id', { user_id })
.andWhere('study_logs.date = :date', { date })
.getRawOne();
return parseInt(sum?.sum ?? 0);
.andWhere('study_logs.date BETWEEN :start_date AND :end_date', {
start_date,
end_date,
})
.groupBy('study_logs.date')
.orderBy('study_logs.date', 'ASC')
.getRawMany();

daily_sums.forEach(({ date, daily_sum }) => {
result[moment(date).diff(startMoment, 'days')] = +daily_sum;
});
return result;
}

async groupByCategory(user_id: number, date: string): Promise<object> {
Expand Down Expand Up @@ -96,7 +111,7 @@ export class StudyLogsService {
}));

const result = {
total_time: await this.calculateTotalTime(user_id, date),
total_time: (await this.calculateTotalTimes(user_id, date, date))[0],
categories,
};
return result;
Expand Down
7 changes: 2 additions & 5 deletions BE/src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { ConfigService } from '@nestjs/config';
import { v4 } from 'uuid';
import { GreenEyeResponse } from './interface/greeneye.interface';
import { S3Service } from 'src/common/s3.service';
import * as path from 'path';
import { ENV } from 'src/common/const/env-keys.const';
import { UpdateUserDto } from './dto/update-user.dto';
import { getImageUrl } from 'src/common/utils';

@Injectable()
export class UsersService {
Expand Down Expand Up @@ -44,10 +44,7 @@ export class UsersService {
if (!user) {
throw new BadRequestException('해당 유저가 존재하지 않습니다.');
}
return path.join(
this.config.get(ENV.CDN_ENDPOINT),
user.image_url ?? 'default.png',
);
return getImageUrl(this.config.get(ENV.CDN_ENDPOINT), user.image_url);
}

async updateUser(
Expand Down

0 comments on commit 8f75778

Please sign in to comment.