Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[백한결] 출석 체크 기록 API #1

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
File renamed without changes.
File renamed without changes.
Empty file modified setup.sh
100644 → 100755
Empty file.
5 changes: 5 additions & 0 deletions template/source/typeorm/app.module.ts → src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { addTransactionalDataSource } from 'typeorm-transactional';
import { DataSource } from 'typeorm';
import { AttendanceModule } from './attendance/attendance.module';
import { User } from './attendance/entities/user.entity';
import { Attendance } from './attendance/entities/attendance.entity';

@Module({
imports: [
Expand All @@ -17,6 +20,7 @@ import { DataSource } from 'typeorm';
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
synchronize: process.env.DB_SYNC === 'true',
entities: [User, Attendance],
timezone: 'Z',
};
},
Expand All @@ -28,6 +32,7 @@ import { DataSource } from 'typeorm';
return addTransactionalDataSource(new DataSource(options));
},
}),
AttendanceModule,
],
controllers: [],
providers: [],
Expand Down
19 changes: 19 additions & 0 deletions src/attendance/attendance.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Controller, Post, Body, Get, Param, Query } from '@nestjs/common';
import { AttendanceService } from './attendance.service';
import { CreateAttendanceRequestDto } from './dto/request/create-attendacne-request.dto';
import { PaginationDto } from '../common/dto/pagination.dto';

@Controller('attendance')
export class AttendanceController {
constructor(private readonly attendanceService: AttendanceService) {}

@Post()
async createAttendance(@Body() request: CreateAttendanceRequestDto) {
return this.attendanceService.createAttendance(request);
}

@Get('/:userId')
async getAttendance(@Param('userId') id: number, @Query() request: PaginationDto){
return this.attendanceService.getAttendance(request, id);
}
}
13 changes: 13 additions & 0 deletions src/attendance/attendance.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { AttendanceService } from './attendance.service';
import { AttendanceController } from './attendance.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Attendance } from './entities/attendance.entity';
import { User } from './entities/user.entity';

@Module({
imports: [TypeOrmModule.forFeature([Attendance, User])],
controllers: [AttendanceController],
providers: [AttendanceService],
})
export class AttendanceModule {}
76 changes: 76 additions & 0 deletions src/attendance/attendance.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Attendance } from './entities/attendance.entity';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { CreateAttendanceRequestDto } from './dto/request/create-attendacne-request.dto';
import { AttendanceStatusEnum } from './entities/attendance-status.enum';
import { CreateAttendanceResponseDto } from './dto/response/create-attendance-response.dto';
import { PaginationDto } from '../common/dto/pagination.dto';
import { createPaginationResult, PaginationResult } from '../common/util/pagination.util';
import { GetAttendanceResponseDto } from './dto/response/get-attendance-response.dto';

@Injectable()
export class AttendanceService {
constructor(
@InjectRepository(Attendance)
private readonly attendanceRepository: Repository<Attendance>,
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {
}

async createAttendance(dto: CreateAttendanceRequestDto) {
const attendanceTime = new Date();
const user = await this.userRepository.findOneBy({ studentId: dto.studentId });
if (!user) {
throw new Error('유저를 찾을 수 없습니다.');
}
const status = this.getAttendanceStatus(attendanceTime);
const attendance = this.attendanceRepository.create({...dto, attendanceTime, status, user});
await this.attendanceRepository.save(attendance);

return CreateAttendanceResponseDto.from(<CreateAttendanceResponseDto>attendance);
}

private getAttendanceStatus(attendanceTime: Date | string): AttendanceStatusEnum {
const attendanceDate = new Date(attendanceTime);

const classStartHour = 9;
const classEndHour = 18;

const hour = attendanceDate.getHours();
const minute = attendanceDate.getMinutes();

if(hour < classStartHour || hour > classEndHour) {
return AttendanceStatusEnum.ABSENT;
}

if (minute >= 51 || minute <= 10)
return AttendanceStatusEnum.PRESENT;
else if (minute >= 11 && minute <= 20)
return AttendanceStatusEnum.LATE;
else if (minute >= 21 && minute <= 50)
return AttendanceStatusEnum.ABSENT;
}

async getAttendance(dto: PaginationDto, userId: number): Promise<PaginationResult<GetAttendanceResponseDto>> {
const date = new Date();
date.setDate(date.getDate() - 30);

const [attendances, total] = await this.attendanceRepository
.createQueryBuilder('attendance')
.leftJoinAndSelect('attendance.user', 'user')
.where('user.id = :userId', { userId })
.andWhere('attendance.attendanceTime > :date', { date })
.orderBy('attendance.attendanceTime', 'DESC')
.skip((dto.page - 1) * dto.limit)
.take(dto.limit)
.getManyAndCount();

const result = attendances.map(attendance => GetAttendanceResponseDto.from(<GetAttendanceResponseDto>attendance));

return createPaginationResult(result, dto.page, dto.limit, total);
}

}
12 changes: 12 additions & 0 deletions src/attendance/dto/request/create-attendacne-request.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IsNotEmpty } from 'class-validator';

export class CreateAttendanceRequestDto {
@IsNotEmpty()
name: string;

@IsNotEmpty()
department: string;

@IsNotEmpty()
studentId: number;
}
20 changes: 20 additions & 0 deletions src/attendance/dto/response/create-attendance-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { AttendanceStatusEnum } from '../../entities/attendance-status.enum';
import { User } from '../../entities/user.entity';

export class CreateAttendanceResponseDto {
id: number;
attendanceTime: Date;
status: AttendanceStatusEnum;
userId: number;
user: User;

public static from(dto: CreateAttendanceResponseDto): CreateAttendanceResponseDto {
const it = new CreateAttendanceResponseDto();
it.id = dto.id;
it.attendanceTime = dto.attendanceTime;
it.status = dto.status;
it.userId = dto.user.id;
it.user = dto.user;
return it;
}
}
21 changes: 21 additions & 0 deletions src/attendance/dto/response/get-attendance-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { AttendanceStatusEnum } from '../../entities/attendance-status.enum';
import { User } from '../../entities/user.entity';

export class GetAttendanceResponseDto {
id: number;
attendanceTime: Date;
status: AttendanceStatusEnum;
userId: number;
user: User;

public static from(dto: GetAttendanceResponseDto): GetAttendanceResponseDto {
const response = new GetAttendanceResponseDto();
response.id = dto.id;
response.attendanceTime = dto.attendanceTime;
response.status = dto.status;
response.userId = dto.user.id;
response.user = dto.user;

return response;
}
}
5 changes: 5 additions & 0 deletions src/attendance/entities/attendance-status.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum AttendanceStatusEnum {
PRESENT = 'PRESENT',
ABSENT = 'ABSENT',
LATE = 'LATE',
}
21 changes: 21 additions & 0 deletions src/attendance/entities/attendance.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Column, Entity, ManyToOne, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
import { User } from './user.entity';
import { AttendanceStatusEnum } from './attendance-status.enum';

@Entity()
export class Attendance {
@PrimaryGeneratedColumn()
id: number;

@Column()
attendanceTime: Date;

@Column({
type: 'enum',
enum: AttendanceStatusEnum,
})
status: AttendanceStatusEnum;

@ManyToOne(() => User, user => user.attendance)
user: User;
}
20 changes: 20 additions & 0 deletions src/attendance/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Attendance } from './attendance.entity';

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;

@Column()
name: string;

@Column()
department: string;

@Column()
studentId: number;

@OneToMany(type => Attendance, attendance => attendance.user)
attendance: Attendance;
}
12 changes: 12 additions & 0 deletions src/common/dto/pagination.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Type } from 'class-transformer';
import { IsInt } from 'class-validator';

export class PaginationDto {
@Type(() => Number)
@IsInt()
page: number = 1;

@Type(() => Number)
@IsInt()
limit: number = 10;
}
24 changes: 24 additions & 0 deletions src/common/util/pagination.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export class PaginationResult<T> {
data: T[];
page: number;
limit: number;
totalCount: number;
totalPage: number;

constructor(data: T[], page: number, limit: number, totalCount: number) {
this.data = data;
this.page = page;
this.limit = limit;
this.totalCount = totalCount;
this.totalPage = Math.ceil(totalCount / limit);
}
}

export function createPaginationResult<T>(
data: T[],
page: number,
limit: number,
totalCount: number
): PaginationResult<T> {
return new PaginationResult(data, page, limit, totalCount);
}
9 changes: 9 additions & 0 deletions template/source/typeorm/main.ts → src/main.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { initializeTransactionalContext } from 'typeorm-transactional';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
initializeTransactionalContext();

const app = await NestFactory.create(AppModule);

app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
)
// TODO: 프로그램 구현
await app.listen(process.env.PORT || 8000);

Expand Down