From 32c71fd95ed55c633c560c4bf72f2a048765d6c8 Mon Sep 17 00:00:00 2001 From: niamu01 Date: Sat, 9 Dec 2023 03:15:37 +0900 Subject: [PATCH] refactor: tag-log-v1 & v2 -> tag-log - #189 --- src/app.module.ts | 4 +- src/external/cabi42/cabi42.module.ts | 4 +- src/external/cabi42/cabi42.service.ts | 2 +- .../cabi42/dto/cabi42.response.dto.ts | 2 +- src/external/where42/where42.module.ts | 2 +- src/external/where42/where42.service.ts | 2 +- .../interface/tag-log-repository.interface.ts | 57 --- src/tag-log-v1/tag-log.controller.ts | 216 ------------ src/tag-log-v1/tag-log.module.ts | 34 -- src/tag-log-v1/tag-log.service.ts | 331 ------------------ src/tag-log-v2/dto/UserInOutLogs.type.ts | 22 -- .../dto/admin/user-accumulation-day.type.ts | 67 ---- .../admin/user-accumulation-month-day.type.ts | 10 - .../dto/admin/user-accumulation-month.type.ts | 10 - src/tag-log-v2/dto/admin/user-id.type.ts | 15 - src/tag-log-v2/dto/inout.dto.ts | 6 - src/tag-log-v2/dto/pair-info.dto.ts | 4 - src/tag-log-v2/dto/tag-log.dto.ts | 6 - src/tag-log-v2/dto/user-Info.type.ts | 47 --- .../pair-info-repository.interface.ts | 18 - .../repository/mysql/pair-info.repository.ts | 26 -- .../repository/mysql/tag-log.repository.ts | 118 ------- src/tag-log-v2/tag-log-v2-admin.controller.ts | 265 -------------- src/tag-log-v2/tag-log-v2-admin.service.ts | 169 --------- .../dto/UserInOutLogs.type.ts | 0 .../dto/admin/user-accumulation-day.type.ts | 0 .../admin/user-accumulation-month-day.type.ts | 0 .../dto/admin/user-accumulation-month.type.ts | 0 .../dto/admin/user-id.type.ts | 0 .../dto/device-info.dto.ts | 0 src/{tag-log-v1 => tag-log}/dto/inout.dto.ts | 0 .../dto/pair-info.dto.ts | 0 .../dto/subType/InOutLog.type.ts | 0 .../dto/subType/InOutLog.type.v1.ts} | 2 +- .../dto/tag-log.dto.ts | 0 .../dto/user-Info.type.ts | 0 .../dto/user-accumulation.type.ts | 0 .../dto/user-accumulation.type.v1.ts} | 2 +- .../device-info-repository.interface.ts | 2 +- .../pair-info-repository.interface.ts | 2 +- .../interface/tag-log-repository.interface.ts | 2 +- .../mysql/device-info.repository.ts | 2 +- .../repository/mysql/pair-info.repository.ts | 6 +- .../repository/mysql/tag-log.repository.ts | 6 +- .../tag-log-admin.controller.ts | 28 +- .../tag-log-admin.service.ts | 12 +- .../tag-log.controller.ts} | 2 +- .../tag-log.module.ts} | 8 +- .../tag-log.service.spec.ts} | 7 +- .../tag-log.service.ts} | 293 +++++++++++++++- 50 files changed, 344 insertions(+), 1467 deletions(-) delete mode 100644 src/tag-log-v1/repository/interface/tag-log-repository.interface.ts delete mode 100644 src/tag-log-v1/tag-log.controller.ts delete mode 100644 src/tag-log-v1/tag-log.module.ts delete mode 100644 src/tag-log-v1/tag-log.service.ts delete mode 100644 src/tag-log-v2/dto/UserInOutLogs.type.ts delete mode 100644 src/tag-log-v2/dto/admin/user-accumulation-day.type.ts delete mode 100644 src/tag-log-v2/dto/admin/user-accumulation-month-day.type.ts delete mode 100644 src/tag-log-v2/dto/admin/user-accumulation-month.type.ts delete mode 100644 src/tag-log-v2/dto/admin/user-id.type.ts delete mode 100644 src/tag-log-v2/dto/inout.dto.ts delete mode 100644 src/tag-log-v2/dto/pair-info.dto.ts delete mode 100644 src/tag-log-v2/dto/tag-log.dto.ts delete mode 100644 src/tag-log-v2/dto/user-Info.type.ts delete mode 100644 src/tag-log-v2/repository/interface/pair-info-repository.interface.ts delete mode 100644 src/tag-log-v2/repository/mysql/pair-info.repository.ts delete mode 100644 src/tag-log-v2/repository/mysql/tag-log.repository.ts delete mode 100644 src/tag-log-v2/tag-log-v2-admin.controller.ts delete mode 100644 src/tag-log-v2/tag-log-v2-admin.service.ts rename src/{tag-log-v1 => tag-log}/dto/UserInOutLogs.type.ts (100%) rename src/{tag-log-v1 => tag-log}/dto/admin/user-accumulation-day.type.ts (100%) rename src/{tag-log-v1 => tag-log}/dto/admin/user-accumulation-month-day.type.ts (100%) rename src/{tag-log-v1 => tag-log}/dto/admin/user-accumulation-month.type.ts (100%) rename src/{tag-log-v1 => tag-log}/dto/admin/user-id.type.ts (100%) rename src/{tag-log-v2 => tag-log}/dto/device-info.dto.ts (100%) rename src/{tag-log-v1 => tag-log}/dto/inout.dto.ts (100%) rename src/{tag-log-v1 => tag-log}/dto/pair-info.dto.ts (100%) rename src/{tag-log-v2 => tag-log}/dto/subType/InOutLog.type.ts (100%) rename src/{tag-log-v1/dto/subType/InOutLog.type.ts => tag-log/dto/subType/InOutLog.type.v1.ts} (93%) rename src/{tag-log-v1 => tag-log}/dto/tag-log.dto.ts (100%) rename src/{tag-log-v1 => tag-log}/dto/user-Info.type.ts (100%) rename src/{tag-log-v2 => tag-log}/dto/user-accumulation.type.ts (100%) rename src/{tag-log-v1/dto/user-accumulation.type.ts => tag-log/dto/user-accumulation.type.v1.ts} (87%) rename src/{tag-log-v2 => tag-log}/repository/interface/device-info-repository.interface.ts (71%) rename src/{tag-log-v1 => tag-log}/repository/interface/pair-info-repository.interface.ts (84%) rename src/{tag-log-v2 => tag-log}/repository/interface/tag-log-repository.interface.ts (96%) rename src/{tag-log-v2 => tag-log}/repository/mysql/device-info.repository.ts (88%) rename src/{tag-log-v1 => tag-log}/repository/mysql/pair-info.repository.ts (92%) rename src/{tag-log-v1 => tag-log}/repository/mysql/tag-log.repository.ts (95%) rename src/{tag-log-v1 => tag-log}/tag-log-admin.controller.ts (98%) rename src/{tag-log-v1 => tag-log}/tag-log-admin.service.ts (96%) rename src/{tag-log-v2/tag-log-v2.controller.ts => tag-log/tag-log.controller.ts} (99%) rename src/{tag-log-v2/tag-log-v2.module.ts => tag-log/tag-log.module.ts} (84%) rename src/{tag-log-v2/tag-log-v2.service.spec.ts => tag-log/tag-log.service.spec.ts} (97%) rename src/{tag-log-v2/tag-log-v2.service.ts => tag-log/tag-log.service.ts} (66%) diff --git a/src/app.module.ts b/src/app.module.ts index 3e06482..4dd88cc 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,8 +11,7 @@ import { SessionMiddleware } from './middleware/session-middleware'; import { RedirectModule } from './redirect/redirect.module'; import { ReissueModule } from './reissue/reissue.module'; import { StatisticsModule } from './statistics/statictics.module'; -import { TagLogModule } from './tag-log-v1/tag-log.module'; -import { TagLogModule2 } from './tag-log-v2/tag-log-v2.module'; +import { TagLogModule2 as TagLogModule } from './tag-log/tag-log.module'; import { UserModule } from './user/user.module'; @Module({ @@ -30,7 +29,6 @@ import { UserModule } from './user/user.module'; RedirectModule, AuthModule, TagLogModule, - TagLogModule2, UserModule, Where42Module, Cabi42Module, diff --git a/src/external/cabi42/cabi42.module.ts b/src/external/cabi42/cabi42.module.ts index 33e139b..b55a1bc 100644 --- a/src/external/cabi42/cabi42.module.ts +++ b/src/external/cabi42/cabi42.module.ts @@ -1,8 +1,8 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { TagLog } from 'src/entities/tag-log.entity'; -import { TagLogRepository } from 'src/tag-log-v1/repository/mysql/tag-log.repository'; -import { TagLogModule2 } from 'src/tag-log-v2/tag-log-v2.module'; +import { TagLogRepository } from 'src/tag-log/repository/mysql/tag-log.repository'; +import { TagLogModule2 } from 'src/tag-log/tag-log.module'; import { UserModule } from 'src/user/user.module'; import { Cabi42Controller } from './cabi42.controller'; import { Cabi42Service } from './cabi42.service'; diff --git a/src/external/cabi42/cabi42.service.ts b/src/external/cabi42/cabi42.service.ts index 2b39bc5..414e788 100644 --- a/src/external/cabi42/cabi42.service.ts +++ b/src/external/cabi42/cabi42.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { TagLogService } from 'src/tag-log-v2/tag-log-v2.service'; +import { TagLogService } from 'src/tag-log/tag-log.service'; import { UserService } from 'src/user/user.service'; import { Cabi42ResponseDto } from './dto/cabi42.response.dto'; diff --git a/src/external/cabi42/dto/cabi42.response.dto.ts b/src/external/cabi42/dto/cabi42.response.dto.ts index 2804271..ac4be8e 100644 --- a/src/external/cabi42/dto/cabi42.response.dto.ts +++ b/src/external/cabi42/dto/cabi42.response.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { UserIdType } from 'src/tag-log-v2/dto/admin/user-id.type'; +import { UserIdType } from 'src/tag-log/dto/admin/user-id.type'; export class Cabi42ResponseDto extends UserIdType { @ApiProperty({ diff --git a/src/external/where42/where42.module.ts b/src/external/where42/where42.module.ts index 5f2d172..a89957f 100644 --- a/src/external/where42/where42.module.ts +++ b/src/external/where42/where42.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { DeviceInfo } from 'src/entities/device-info.entity'; import { TagLog } from 'src/entities/tag-log.entity'; -import { TagLogRepository } from 'src/tag-log-v1/repository/mysql/tag-log.repository'; +import { TagLogRepository } from 'src/tag-log/repository/mysql/tag-log.repository'; import { UserModule } from 'src/user/user.module'; import { DeviceInfoRepository } from './repository/mysql/device-info.repository'; import { Where42Controller } from './where42.controller'; diff --git a/src/external/where42/where42.service.ts b/src/external/where42/where42.service.ts index 0b219ae..4b0de3b 100644 --- a/src/external/where42/where42.service.ts +++ b/src/external/where42/where42.service.ts @@ -5,7 +5,7 @@ import { Injectable, Logger, } from '@nestjs/common'; -import { ITagLogRepository } from 'src/tag-log-v1/repository/interface/tag-log-repository.interface'; +import { ITagLogRepository } from 'src/tag-log/repository/interface/tag-log-repository.interface'; import { UserService } from 'src/user/user.service'; import { Where42ResponseDto } from './dto/where42.response.dto'; import { IDeviceInfoRepository } from './repository/interface/device-info.repository.interface'; diff --git a/src/tag-log-v1/repository/interface/tag-log-repository.interface.ts b/src/tag-log-v1/repository/interface/tag-log-repository.interface.ts deleted file mode 100644 index 7d0d0a8..0000000 --- a/src/tag-log-v1/repository/interface/tag-log-repository.interface.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { TagLogDto } from 'src/tag-log-v1/dto/tag-log.dto'; -import { CardDto } from 'src/user/dto/card.dto'; - -export interface ITagLogRepository { - /** - * card ID들과 특정 기간에 속하는 태그 로그들을 가져옵니다. - * (deprecated) - * - * @param cardIDs 카드 ID 배열 - * @param start 기간 시작 - * @param end 기간 끝 - */ - findTagLogs(cardIDs: string[], start: Date, end: Date): Promise; - - /** - * 가져올 기간과 card 정보 (카드 아이디, 발급시간, 만료시간) 에 속하는 태그 로그들을 가져옵니다. - * - * @param cardIDs 카드 ID 배열 - * @param start 기간 시작 - * @param end 기간 끝 - */ - findTagLogsByCards( - cards: CardDto[], - start: Date, - end: Date, - ): Promise; - - /** - * 특정 card ID들에 대해 가장 최신의 태그 로그를 가져옵니다. 없다면 null을 반환합니다. - * - * @param cards 카드 ID 배열 - */ - findLatestTagLog(cards: CardDto[]): Promise; - - /** - * 특정 card ID들에 대해 가장 오래된 태그 로그를 가져옵니다. 없다면 null을 반환합니다. - * - * @param cards 카드 ID 배열 - */ - findFirstTagLog(cards: CardDto[]): Promise; - - /** - * 특정 카드로 태그한 특정 출입태그 시간의 바로 이전 태그 로그를 가져옵니다. 없다면 null을 반환합니다. - * - * @param date 출입태그 시간 - * @param cardID 카드 ID - */ - findPrevTagLog(cardID: CardDto, date: Date): Promise; - - /** - * 특정 카드로 태그한 특정 출입태그 시간의 바로 다음 태그 로그를 가져옵니다. 없다면 null을 반환합니다. - * - * @param date 출입태그 시간 - * @param cardID 카드 ID - */ - findNextTagLog(cardID: CardDto, date: Date): Promise; -} diff --git a/src/tag-log-v1/tag-log.controller.ts b/src/tag-log-v1/tag-log.controller.ts deleted file mode 100644 index 500a147..0000000 --- a/src/tag-log-v1/tag-log.controller.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { - Controller, - Get, - Logger, - ParseIntPipe, - Query, - UseGuards, -} from '@nestjs/common'; -import { - ApiBearerAuth, - ApiOperation, - ApiQuery, - ApiResponse, - ApiTags, -} from '@nestjs/swagger'; -import { User } from 'src/auth/user.decorator'; -import { UserSessionDto } from 'src/auth/dto/user.session.dto'; -import { UserAccumulationType } from './dto/user-accumulation.type'; -import { UserInfoType } from './dto/user-Info.type'; -import { UserInOutLogsType } from './dto/UserInOutLogs.type'; -import { TagLogService } from './tag-log.service'; -import { UserAuthGuard } from 'src/auth/guard/user-auth.guard'; - -@ApiTags('체류 시간 산출') -@Controller({ - version: '1', - path: 'tag-log', -}) -@ApiBearerAuth() -@UseGuards(UserAuthGuard) -export class TagLogController { - private logger = new Logger(TagLogController.name); - - constructor(private tagLogService: TagLogService) {} - - /** - * 특정 일에 대해 태깅했던 로그를 조회합니다. - * - * @param user 로그인한 사용자 세션 - * @returns UserInOutLogsType - */ - @ApiOperation({ - summary: '일별 태그로그 조회', - description: '일별 짝이 맞는 태그로그를 조회합니다.', - }) - @ApiResponse({ - status: 200, - type: UserInOutLogsType, - description: '조회 성공', - }) - @ApiResponse({ status: 400, description: '쿼리 타입 에러' }) - @ApiResponse({ status: 401, description: '접근 권한 없음' }) - @ApiResponse({ - status: 500, - description: '서버 내부 에러 (백앤드 관리자 문의 필요)', - }) - @ApiQuery({ - name: 'year', - description: '년도', - required: true, - }) - @ApiQuery({ - name: 'month', - description: '월', - required: true, - }) - @ApiQuery({ - name: 'day', - description: '일', - required: true, - }) - @Get('perday') - async getPerDay( - @User() user: UserSessionDto, - @Query('year', ParseIntPipe) year: number, - @Query('month', ParseIntPipe) month: number, - @Query('day', ParseIntPipe) day: number, - ): Promise { - this.logger.debug(`@getPerDay) ${year}-${month}-${day} by ${user.login}`); - - const date = new Date(`${year}-${month}-${day}`); - - const results = await this.tagLogService.getPerDay(user.user_id, date); - return { - login: user.login, - profileImage: user.image_url, - inOutLogs: results, - }; - } - - /** - * 특정 월에 대해 체류했던 시간을 조회합니다. - * - * @param user 로그인한 사용자 세션 - * @returns UsageResponseDto - */ - @ApiOperation({ - summary: '월별 태그로그 조회', - description: '월별 짝이 맞는 태그로그를 조회합니다.', - }) - @ApiResponse({ - status: 200, - type: UserInOutLogsType, - description: '조회 성공', - }) - @ApiResponse({ status: 400, description: '쿼리 타입 에러' }) - @ApiResponse({ status: 401, description: '접근 권한 없음' }) - @ApiResponse({ - status: 500, - description: '서버 내부 에러 (백앤드 관리자 문의 필요)', - }) - @ApiQuery({ - name: 'year', - description: '년도', - required: true, - }) - @ApiQuery({ - name: 'month', - description: '월', - required: true, - }) - @Get('permonth') - async getPerMonth( - @User() user: UserSessionDto, - @Query('year', ParseIntPipe) year: number, - @Query('month', ParseIntPipe) month: number, - ): Promise { - this.logger.debug(`@getPerMonth) ${year}-${month} by ${user.login}`); - - const date = new Date(`${year}-${month}`); - - const results = await this.tagLogService.getPerMonth(user.user_id, date); - return { - login: user.login, - profileImage: user.image_url, - inOutLogs: results, - }; - } - - /** - * 로그인한 유저가 메인 화면에 접속할 때 가져올 정보를 반환합니다. - */ - @ApiOperation({ - summary: '사용자 접속 시 보여줄 메인 정보', - description: - '로그인한 유저가 메인 화면에 접속할 때 가져올 정보를 조회합니다.', - }) - @ApiResponse({ - status: 200, - type: UserInfoType, - description: '조회 성공', - }) - @ApiResponse({ status: 401, description: '접근 권한 없음' }) - @ApiResponse({ - status: 500, - description: '서버 내부 에러 (백앤드 관리자 문의 필요)', - }) - @Get('maininfo') - async getMainInfo(@User() user: UserSessionDto): Promise { - this.logger.debug(`@getMainInfo) by ${user.login}`); - const inoutState = await this.tagLogService.checkClusterById(user.user_id); - const result: UserInfoType = { - login: user.login, - profileImage: user.image_url, - isAdmin: user.is_staff, - inoutState: inoutState.inout, - tagAt: inoutState.log, - }; - return result; - } - - /** - * 로그인한 유저의 일별/월별 누적 체류시간을 반환합니다. - */ - @ApiOperation({ - summary: '로그인한 유저의 일별/월별 누적 체류시간', - description: '로그인한 유저의 일별/월별 누적 체류시간을 조회합니다.', - }) - @ApiResponse({ - status: 200, - type: UserAccumulationType, - description: '조회 성공', - }) - @ApiResponse({ status: 401, description: '접근 권한 없음' }) - @ApiResponse({ - status: 500, - description: '서버 내부 에러 (백앤드 관리자 문의 필요)', - }) - @Get('accumulationTimes') - async getAccumulationTimes( - @User() user: UserSessionDto, - ): Promise { - this.logger.debug(`@getAccumulationTimes) by ${user.login}`); - const date = new Date(); - const resultDay = await this.tagLogService.getPerDay(user.user_id, date); - const resultMonth = await this.tagLogService.getPerMonth( - user.user_id, - date, - ); - - const resultDaySum = resultDay.reduce( - (prev, result) => result.durationSecond + prev, - 0, - ); - const resultMonthSum = resultMonth.reduce( - (prev, result) => result.durationSecond + prev, - 0, - ); - - const result: UserAccumulationType = { - todayAccumationTime: resultDaySum, - monthAccumationTime: resultMonthSum, - }; - return result; - } -} diff --git a/src/tag-log-v1/tag-log.module.ts b/src/tag-log-v1/tag-log.module.ts deleted file mode 100644 index 0de3140..0000000 --- a/src/tag-log-v1/tag-log.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { DateCalculatorModule } from 'src/data-calculator/data-calculator.module'; -import { PairInfo } from 'src/entities/pair-info.entity'; -import { TagLog } from 'src/entities/tag-log.entity'; -import { UserModule } from 'src/user/user.module'; -import { PairInfoRepository } from './repository/mysql/pair-info.repository'; -import { TagLogRepository } from './repository/mysql/tag-log.repository'; -import { TagLogAdminController } from './tag-log-admin.controller'; -import { TagLogAdminService } from './tag-log-admin.service'; -import { TagLogController } from './tag-log.controller'; -import { TagLogService } from './tag-log.service'; - -const tagLogRepo = { - provide: 'ITagLogRepository', - useClass: TagLogRepository, -}; - -const pairInfoRepo = { - provide: 'IPairInfoRepository', - useClass: PairInfoRepository, -}; - -@Module({ - imports: [ - TypeOrmModule.forFeature([TagLog, PairInfo]), - DateCalculatorModule, - UserModule, - ], - exports: [TypeOrmModule], - controllers: [TagLogController, TagLogAdminController], - providers: [tagLogRepo, pairInfoRepo, TagLogService, TagLogAdminService], -}) -export class TagLogModule {} diff --git a/src/tag-log-v1/tag-log.service.ts b/src/tag-log-v1/tag-log.service.ts deleted file mode 100644 index 2942cd9..0000000 --- a/src/tag-log-v1/tag-log.service.ts +++ /dev/null @@ -1,331 +0,0 @@ -import { Inject, Injectable, Logger } from '@nestjs/common'; -import InOut from 'src/enums/inout.enum'; -import { UserService } from 'src/user/user.service'; -import { DateCalculator } from 'src/data-calculator/date-calculator.component'; -import { InOutDto } from './dto/inout.dto'; -import { TagLogDto } from './dto/tag-log.dto'; -import { PairInfoDto } from './dto/pair-info.dto'; -import { InOutLogType } from './dto/subType/InOutLog.type'; -import { ITagLogRepository } from './repository/interface/tag-log-repository.interface'; -import { IPairInfoRepository } from './repository/interface/pair-info-repository.interface'; -import { CardDto } from 'src/user/dto/card.dto'; - -@Injectable() -export class TagLogService { - private logger = new Logger(TagLogService.name); - - constructor( - private userService: UserService, - @Inject('ITagLogRepository') - private tagLogRepository: ITagLogRepository, - @Inject('IPairInfoRepository') - private pairInfoRepository: IPairInfoRepository, - private dateCalculator: DateCalculator, - ) {} - - /** - * 카드 태그 로그에 대해 로그 맨 앞, 맨 뒤 원소가 잘려 있다면 앞, 뒤에 가상의 입출입 로그를 삽입합니다. - * 삽입하는 로그는 짝 여부에 관계없이 전후 원소를 삽입합니다. - * 짝 일치 여부 판단은 다른 로직에서 진행합니다. - * version 2: 카드의 짝을 맞추도록 수정하였습니다. - * version 3: 이전/이후 로그를 가져올 때, 카드의 유효 기간도 고려하도록 변경하였습니다. - * - * @param taglogs TagLogDto[] - * @return TagLogDto[] - * @version 3 - */ - async trimTagLogs( - taglogs: TagLogDto[], - cards: CardDto[], - start: Date, - end: Date, - ): Promise { - this.logger.debug(`@trimTagLogs)`); - // 1. 맨 앞의 로그를 가져옴. - const firstLog = taglogs.at(0); - if (firstLog) { - // 2. 맨 앞의 로그 이전의 로그를 가져옴. - const beforeFirstLog = await this.tagLogRepository.findPrevTagLog( - cards.find((v) => v.card_id === firstLog.card_id), - firstLog.tag_at, - ); - // NOTE: tag log에 기록된 첫번째 로그가 퇴실인 경우 현재는 짝을 맞추지 않음. - if (beforeFirstLog !== null) { - const virtualEnterTime = this.dateCalculator.getStartOfDate(start); - taglogs.unshift({ - tag_at: virtualEnterTime, - device_id: beforeFirstLog.device_id, - idx: -1, - card_id: beforeFirstLog.card_id, - }); - } - } - // 3. 맨 뒤의 로그를 가져옴. - const lastLog = taglogs.at(-1); - if (lastLog) { - // 6. 맨 뒤의 로그 이후의 로그를 가져옴. - const afterLastLog = await this.tagLogRepository.findNextTagLog( - cards.find((v) => v.card_id === lastLog.card_id), - lastLog.tag_at, - ); - // NOTE: 현재는 카뎃의 현재 입실여부에 관계없이 짝을 맞춤. - if (afterLastLog !== null) { - const virtualLeaveTime = this.dateCalculator.getEndOfDate(end); - taglogs.push({ - tag_at: virtualLeaveTime, - device_id: afterLastLog.device_id, - idx: -1, - card_id: afterLastLog.card_id, - }); - } - } - return taglogs; - } - - /** - * 인자로 들어간 디바이스가 쌍이 맞는지 확인하는 함수입니다. - * - * @param deviceInfo - * @param enterDevice - * @param device - */ - validateDevicePair( - deviceInfos: PairInfoDto[], - inDevice: number, - outDevice: number, - ): boolean { - //this.logger.debug(`@validateDevicePair) ${inDevice} - ${outDevice}`); - // TODO: O(N) 보다 더 적게 시간을 소요하도록 리팩터링 필요 - const find = deviceInfos.find( - (device) => - device.in_device === inDevice && device.out_device === outDevice, - ); - return !!find; - } - - /** - * 기기 쌍 정보와 태깅한 로그들을 이용하여 출입 로그를 반환합니다. - * - * @param taglogs 입출입 로그 - * @param deviceInfos 기기 짝 정보 - */ - getPairsByTagLogs( - taglogs: TagLogDto[], - deviceInfos: PairInfoDto[], - ): InOutLogType[] { - this.logger.debug(`@getPairsByTagLogs)`); - - /** - * 데이터를 날짜의 내림차순으로 정렬 - */ - // const timeLines = taglogs - // //.sort((a, b) => (a.tag_at < b.tag_at ? 1 : -1)) - // .map((v) => ({ - // tag_at: v.tag_at, - // device_id: v.device_id, - // })); - const timeLines = taglogs; - - let leave: TagLogDto | null = null; - const resultPairs: InOutLogType[] = []; - - // 타임라인 배열이 빌 때까지 루프를 돌립니다. - while (timeLines.length > 0) { - // 가장 뒤의 원소(가장 늦은 날짜)를 뽑습니다. - const val = timeLines.pop(); - - // 만약 퇴장로그로 가정한 로그가 없다면 뽑은 원소를 넣고 루프를 다시 실행합니다. - if (leave === null) { - leave = val; - continue; - } - - // 뽑아낸 원소를 임시로 저장합니다. - const temp: TagLogDto | null = val; - - // 퇴장로그로 가정한 로그와 현재 뽑아낸 로그가 기기 짝이 맞는지 확인합니다. - if ( - this.validateDevicePair(deviceInfos, temp.device_id, leave.device_id) - ) { - // 기기 짝이 맞을 경우 임시 원소를 입장 로그로 지정합니다. - const enter = temp; - - // 입장 로그와 퇴장 로그가 짝이 맞는지 확인합니다. - if (this.dateCalculator.checkEqualDay(enter.tag_at, leave.tag_at)) { - // 만약 동일한 날짜에 속한다면 해당 쌍이 짝이 됩니다. - const inTimeStamp = this.dateCalculator.toTimestamp(enter.tag_at); - const outTimeStamp = this.dateCalculator.toTimestamp(leave.tag_at); - resultPairs.push({ - inTimeStamp, - outTimeStamp, - durationSecond: outTimeStamp - inTimeStamp, - }); - } else { - // 만약 입장 로그와 퇴장 로그가 짝이 맞지만, 동일한 날짜에 속하지 않으면 일 단위로 자릅니다. - - // 퇴장 로그의 정시 (00시)를 기준으로 두 날짜의 간격을 자릅니다. 두 날짜는 가상의 입퇴장 짝이 됩니다. - const virtualEnterTime = this.dateCalculator.getStartOfDate( - leave.tag_at, - ); - const virtualLeaveTime = this.dateCalculator.getEndOfLastDate( - leave.tag_at, - ); - - // 가상 입장시간 (퇴장시간의 날짜의 정시 - 00시)과 퇴장시간이 짝을 맞춥니다. - const inTimeStamp = this.dateCalculator.toTimestamp(virtualEnterTime); - const outTimeStamp = this.dateCalculator.toTimestamp(leave.tag_at); - resultPairs.push({ - inTimeStamp, - outTimeStamp, - durationSecond: outTimeStamp - inTimeStamp, - }); - - // 그리고 가상 퇴장시간을 다시 배열에 넣어 가상 퇴장시간과 맞는 짝을 찾습니다. - timeLines.push(val); - timeLines.push({ - tag_at: virtualLeaveTime, - device_id: leave.device_id, - idx: -1, - card_id: val.card_id, - }); - } - - // 퇴장로그로 가정한 로그를 초기화합니다. - leave = null; - } else { - // pair가 아닌 경우 뽑아낸 로그를 퇴장로그로 가정하고 루프를 다시 실행합니다. - leave = temp; - } - } - - return resultPairs; - } - - /** - * 인자로 들어가는 사용자 ID와 날짜에 대한 일별 누적시간을 반환합니다. - * - * @param userId 사용자 ID - * @param date 날짜 - * @returns InOutLogType[] - */ - async getPerDay(userId: number, date: Date): Promise { - this.logger.debug(`@getPerDay) ${userId}, ${date}`); - const tagStart = this.dateCalculator.getStartOfDate(date); - const tagEnd = this.dateCalculator.getEndOfDate(date); - - const pairs = await this.pairInfoRepository.findAll(); - - const cards = await this.userService.findCardsByUserId( - userId, - tagStart, - tagEnd, - ); - - const tagLogs = await this.tagLogRepository.findTagLogsByCards( - cards, - tagStart, - tagEnd, - ); - - const sortedTagLogs = tagLogs.sort((a, b) => - a.tag_at > b.tag_at ? 1 : -1, - ); - - // FIXME: 임시 조치임 - const filteredTagLogs = sortedTagLogs.filter( - (v) => v.device_id !== 35 && v.device_id !== 16, - ); - - const trimmedTagLogs = await this.trimTagLogs( - filteredTagLogs, - cards, - tagStart, - tagEnd, - ); - - const resultPairs = this.getPairsByTagLogs(trimmedTagLogs, pairs); - - return resultPairs; - } - - /** - * 인자로 들어가는 사용자 ID와 날짜에 대한 월별 누적시간을 반환합니다. - * - * @param userId 사용자 ID - * @param date 날짜 - * @returns InOutLogType[] - */ - async getPerMonth(userId: number, date: Date): Promise { - this.logger.debug(`@getPerMonth) ${userId}, ${date}`); - const tagStart = this.dateCalculator.getStartOfMonth(date); - const tagEnd = this.dateCalculator.getEndOfMonth(date); - - const pairs = await this.pairInfoRepository.findAll(); - - const cards = await this.userService.findCardsByUserId( - userId, - tagStart, - tagEnd, - ); - - const tagLogs = await this.tagLogRepository.findTagLogsByCards( - cards, - tagStart, - tagEnd, - ); - - const sortedTagLogs = tagLogs.sort((a, b) => - a.tag_at > b.tag_at ? 1 : -1, - ); - - // FIXME: 임시 조치임 - const filteredTagLogs = sortedTagLogs.filter( - (v) => v.device_id !== 35 && v.device_id !== 16, - ); - - const trimmedTagLogs = await this.trimTagLogs( - filteredTagLogs, - cards, - tagStart, - tagEnd, - ); - - const resultPairs = this.getPairsByTagLogs(trimmedTagLogs, pairs); - - return resultPairs; - } - - /** - * 사용자가 클러스터에 체류중인지 확인합니다. - * - * @param userId 사용자 ID - * @returns InOutDto - */ - async checkClusterById(userId: number): Promise { - this.logger.debug(`@checkClusterById) ${userId}`); - const cards = await this.userService.findCardsByUserId( - userId, - new Date('2019-01-01 00:00:00'), - new Date(), // NOTE: 대략 42 클러스터 오픈일부터 지금까지 조회 - ); - const last = await this.tagLogRepository.findLatestTagLog(cards); - const inCards = await this.pairInfoRepository.findInGates(); - - if (last === null) { - return { - log: null, - inout: InOut.OUT, - }; - } - - const inout = - inCards.find((card) => card === last.device_id) === undefined - ? InOut.OUT - : InOut.IN; - - return { - log: last.tag_at, - inout: inout, - }; - } -} diff --git a/src/tag-log-v2/dto/UserInOutLogs.type.ts b/src/tag-log-v2/dto/UserInOutLogs.type.ts deleted file mode 100644 index 0330af1..0000000 --- a/src/tag-log-v2/dto/UserInOutLogs.type.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { InOutLogType } from './subType/InOutLog.type'; - -export class UserInOutLogsType { - @ApiProperty({ - description: '42 로그인 ID', - example: 'joopark', - }) - login: string; - - @ApiProperty({ - description: '인트라 이미지 URI', - example: 'https://cdn.intra.42.fr/users/joopark.jpg', - }) - profileImage: string; - - @ApiProperty({ - description: '입실 - 퇴실 정보 배열', - type: [InOutLogType], - }) - inOutLogs: InOutLogType[]; -} diff --git a/src/tag-log-v2/dto/admin/user-accumulation-day.type.ts b/src/tag-log-v2/dto/admin/user-accumulation-day.type.ts deleted file mode 100644 index 6500712..0000000 --- a/src/tag-log-v2/dto/admin/user-accumulation-day.type.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { UserIdType } from './user-id.type'; - -export class UserAccumulationDayType extends UserIdType { - @ApiProperty({ description: '1일 누적시간 (초)', example: 42 }) - day_1: number; - @ApiProperty({ description: '2일 누적시간 (초)', example: 42 }) - day_2: number; - @ApiProperty({ description: '3일 누적시간 (초)', example: 42 }) - day_3: number; - @ApiProperty({ description: '4일 누적시간 (초)', example: 42 }) - day_4: number; - @ApiProperty({ description: '5일 누적시간 (초)', example: 42 }) - day_5: number; - @ApiProperty({ description: '6일 누적시간 (초)', example: 42 }) - day_6: number; - @ApiProperty({ description: '7일 누적시간 (초)', example: 42 }) - day_7: number; - @ApiProperty({ description: '8일 누적시간 (초)', example: 42 }) - day_8: number; - @ApiProperty({ description: '9일 누적시간 (초)', example: 42 }) - day_9: number; - @ApiProperty({ description: '10일 누적시간 (초)', example: 42 }) - day_10: number; - @ApiProperty({ description: '11일 누적시간 (초)', example: 42 }) - day_11: number; - @ApiProperty({ description: '12일 누적시간 (초)', example: 42 }) - day_12: number; - @ApiProperty({ description: '13일 누적시간 (초)', example: 42 }) - day_13: number; - @ApiProperty({ description: '14일 누적시간 (초)', example: 42 }) - day_14: number; - @ApiProperty({ description: '15일 누적시간 (초)', example: 42 }) - day_15: number; - @ApiProperty({ description: '16일 누적시간 (초)', example: 42 }) - day_16: number; - @ApiProperty({ description: '17일 누적시간 (초)', example: 42 }) - day_17: number; - @ApiProperty({ description: '18일 누적시간 (초)', example: 42 }) - day_18: number; - @ApiProperty({ description: '19일 누적시간 (초)', example: 42 }) - day_19: number; - @ApiProperty({ description: '20일 누적시간 (초)', example: 42 }) - day_20: number; - @ApiProperty({ description: '21일 누적시간 (초)', example: 42 }) - day_21: number; - @ApiProperty({ description: '22일 누적시간 (초)', example: 42 }) - day_22: number; - @ApiProperty({ description: '23일 누적시간 (초)', example: 42 }) - day_23: number; - @ApiProperty({ description: '24일 누적시간 (초)', example: 42 }) - day_24: number; - @ApiProperty({ description: '25일 누적시간 (초)', example: 42 }) - day_25: number; - @ApiProperty({ description: '26일 누적시간 (초)', example: 42 }) - day_26: number; - @ApiProperty({ description: '27일 누적시간 (초)', example: 42 }) - day_27: number; - @ApiProperty({ description: '28일 누적시간 (초)', example: 42 }) - day_28: number; - @ApiPropertyOptional({ description: '29일 누적시간 (초)', example: 42 }) - day_29?: number; - @ApiPropertyOptional({ description: '30일 누적시간 (초)', example: 42 }) - day_30?: number; - @ApiPropertyOptional({ description: '31일 누적시간 (초)', example: 42 }) - day_31?: number; -} diff --git a/src/tag-log-v2/dto/admin/user-accumulation-month-day.type.ts b/src/tag-log-v2/dto/admin/user-accumulation-month-day.type.ts deleted file mode 100644 index e2d3e85..0000000 --- a/src/tag-log-v2/dto/admin/user-accumulation-month-day.type.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { UserAccumulationDayType } from './user-accumulation-day.type'; - -export class UserAccumulationMonthDayType extends UserAccumulationDayType { - @ApiProperty({ - description: '월별 누적시간 (초 단위)', - example: 12345, - }) - monthAccumationTime: number; -} diff --git a/src/tag-log-v2/dto/admin/user-accumulation-month.type.ts b/src/tag-log-v2/dto/admin/user-accumulation-month.type.ts deleted file mode 100644 index 2c85308..0000000 --- a/src/tag-log-v2/dto/admin/user-accumulation-month.type.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { UserIdType } from './user-id.type'; - -export class UserAccumulationMonthType extends UserIdType { - @ApiProperty({ - description: '월별 누적시간 (초 단위)', - example: 12345, - }) - monthAccumationTime: number; -} diff --git a/src/tag-log-v2/dto/admin/user-id.type.ts b/src/tag-log-v2/dto/admin/user-id.type.ts deleted file mode 100644 index 3efaa61..0000000 --- a/src/tag-log-v2/dto/admin/user-id.type.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; - -export class UserIdType { - @ApiProperty({ - description: '42 계정 고유 ID', - example: 12345, - }) - id: number; - - @ApiProperty({ - description: '42 로그인 ID', - example: 'joopark', - }) - login: string; -} diff --git a/src/tag-log-v2/dto/inout.dto.ts b/src/tag-log-v2/dto/inout.dto.ts deleted file mode 100644 index 8e76690..0000000 --- a/src/tag-log-v2/dto/inout.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import InOut from 'src/enums/inout.enum'; - -export class InOutDto { - log: Date | null; - inout: InOut; -} diff --git a/src/tag-log-v2/dto/pair-info.dto.ts b/src/tag-log-v2/dto/pair-info.dto.ts deleted file mode 100644 index 426e5cd..0000000 --- a/src/tag-log-v2/dto/pair-info.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class PairInfoDto { - in_device: number; - out_device: number; -} diff --git a/src/tag-log-v2/dto/tag-log.dto.ts b/src/tag-log-v2/dto/tag-log.dto.ts deleted file mode 100644 index 65d248a..0000000 --- a/src/tag-log-v2/dto/tag-log.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class TagLogDto { - idx: number; - tag_at: Date; - card_id: string; - device_id: number; -} diff --git a/src/tag-log-v2/dto/user-Info.type.ts b/src/tag-log-v2/dto/user-Info.type.ts deleted file mode 100644 index 55d33a6..0000000 --- a/src/tag-log-v2/dto/user-Info.type.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import InOut from 'src/enums/inout.enum'; - -export class UserInfoType { - @ApiProperty({ - description: '42 로그인 ID', - example: 'joopark', - }) - login: string; - - @ApiProperty({ - description: '인트라 이미지 URI', - example: 'https://cdn.intra.42.fr/users/joopark.jpg', - }) - profileImage: string; - - @ApiProperty({ - description: '42 관리자 여부', - example: false, - }) - isAdmin: boolean; - - @ApiPropertyOptional({ - description: '개포 체류인원 (Optional)', - example: 42, - }) - gaepo?: number; - - @ApiPropertyOptional({ - description: '서초 체류인원 (Optional)', - example: 42, - }) - seocho?: number; - - @ApiProperty({ - description: '본인의 클러스터 체류 여부', - enum: ['IN', 'OUT'], - example: 'OUT', - }) - inoutState: InOut; - - @ApiProperty({ - description: '가장 최근에 태깅한 기록 (태깅한 기록이 없다면 null)', - example: null, - }) - tagAt: Date; -} diff --git a/src/tag-log-v2/repository/interface/pair-info-repository.interface.ts b/src/tag-log-v2/repository/interface/pair-info-repository.interface.ts deleted file mode 100644 index 84f68c5..0000000 --- a/src/tag-log-v2/repository/interface/pair-info-repository.interface.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { PairInfoDto } from 'src/tag-log-v1/dto/pair-info.dto'; - -export interface IPairInfoRepository { - /** - * 모든 입출입 게이트 쌍을 반환합니다. - */ - findAll(): Promise; - - /** - * 모든 입장 게이트를 반환합니다. - */ - findInGates(): Promise; - - /** - * 모든 퇴장 게이트를 반환합니다. - */ - findOutGates(): Promise; -} diff --git a/src/tag-log-v2/repository/mysql/pair-info.repository.ts b/src/tag-log-v2/repository/mysql/pair-info.repository.ts deleted file mode 100644 index d6f286b..0000000 --- a/src/tag-log-v2/repository/mysql/pair-info.repository.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; -import { PairInfoDto } from 'src/tag-log-v1/dto/pair-info.dto'; -import { IPairInfoRepository } from '../interface/pair-info-repository.interface'; -import { Repository } from 'typeorm'; -import { PairInfo } from 'src/entities/pair-info.entity'; - -export class PairInfoRepository implements IPairInfoRepository { - constructor( - @InjectRepository(PairInfo) - private pairInfoRepository: Repository, - ) {} - - async findAll(): Promise { - return await this.pairInfoRepository.find(); - } - - async findInGates(): Promise { - const result = await this.pairInfoRepository.find(); - return result.map((col) => col.in_device); - } - - async findOutGates(): Promise { - const result = await this.pairInfoRepository.find(); - return result.map((col) => col.out_device); - } -} diff --git a/src/tag-log-v2/repository/mysql/tag-log.repository.ts b/src/tag-log-v2/repository/mysql/tag-log.repository.ts deleted file mode 100644 index 1813194..0000000 --- a/src/tag-log-v2/repository/mysql/tag-log.repository.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; -import { TagLogDto } from 'src/tag-log-v1/dto/tag-log.dto'; -import { ITagLogRepository } from '../interface/tag-log-repository.interface'; -import { Repository, In, Between, LessThan, MoreThan } from 'typeorm'; -import { TagLog } from 'src/entities/tag-log.entity'; -import { CardDto } from 'src/user/dto/card.dto'; - -export class TagLogRepository implements ITagLogRepository { - constructor( - @InjectRepository(TagLog) - private tagLogRepository: Repository, - ) {} - - async findTagLogs( - cardIDs: string[], - start: Date, - end: Date, - ): Promise { - const result = await this.tagLogRepository.find({ - where: { - card_id: In(cardIDs), - tag_at: Between(start, end), - }, - order: { - tag_at: 'ASC', - }, - }); - return result; - } - - async findTagLogsByCards( - cards: CardDto[], - start: Date, - end: Date, - ): Promise { - const querys = cards.map((card) => { - const search_start = start < card.begin ? card.begin : start; - const search_end = end < card.end ? end : card.end; - return this.tagLogRepository.find({ - where: { - card_id: card.card_id, - tag_at: Between(search_start, search_end), - }, - order: { - tag_at: 'ASC', - }, - }); - }); - const result = await Promise.all(querys); - return result.reduce((prev, next) => prev.concat(next), []); - } - - async findLatestTagLog(cards: CardDto[]): Promise { - if (cards.length === 0) { - return null; - } - const wheres = cards.map((card) => ({ - card_id: card.card_id, - tag_at: Between(card.begin, card.end), - })); - const result = await this.tagLogRepository.findOne({ - where: wheres, - order: { - tag_at: 'DESC', - }, - }); - return result; - } - - async findFirstTagLog(cards: CardDto[]): Promise { - if (cards.length === 0) { - return null; - } - const wheres = cards.map((card) => ({ - card_id: card.card_id, - tag_at: Between(card.begin, card.end), - })); - const result = await this.tagLogRepository.findOne({ - where: wheres, - order: { - tag_at: 'ASC', - }, - }); - return result; - } - - async findPrevTagLog(cardID: CardDto, date: Date): Promise { - const result = await this.tagLogRepository.findOne({ - where: { - card_id: cardID.card_id, - tag_at: LessThan(date), - }, - order: { - idx: 'DESC', - }, - }); - if (result && cardID.begin.getTime() > result.tag_at.getTime()) { - return null; - } - return result; - } - - async findNextTagLog(cardID: CardDto, date: Date): Promise { - const result = await this.tagLogRepository.findOne({ - where: { - card_id: cardID.card_id, - tag_at: MoreThan(date), - }, - order: { - idx: 'ASC', - }, - }); - if (result && cardID.end.getTime() < result.tag_at.getTime()) { - return null; - } - return result; - } -} diff --git a/src/tag-log-v2/tag-log-v2-admin.controller.ts b/src/tag-log-v2/tag-log-v2-admin.controller.ts deleted file mode 100644 index 389af61..0000000 --- a/src/tag-log-v2/tag-log-v2-admin.controller.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { - BadRequestException, - Controller, - Get, - Logger, - Param, - ParseIntPipe, - Query, - UnauthorizedException, - UseGuards, -} from '@nestjs/common'; -import { - ApiBearerAuth, - ApiOperation, - ApiParam, - ApiQuery, - ApiResponse, - ApiTags, -} from '@nestjs/swagger'; -import { User } from 'src/auth/user.decorator'; -import { UserSessionDto } from 'src/auth/dto/user.session.dto'; -import { UserAccumulationDayType } from './dto/admin/user-accumulation-day.type'; -import { UserAccumulationMonthType } from './dto/admin/user-accumulation-month.type'; -import { TagLogAdminService } from './tag-log-v2-admin.service'; -import { AdminAuthGuard } from 'src/auth/guard/admin-auth.guard'; - -@ApiTags('체류 시간 산출 V2 (관리자 전용 API)') -@ApiBearerAuth() -@UseGuards(AdminAuthGuard) -@Controller({ - version: '2', - path: 'tag-log/admin', -}) -export class TagLogAdminController { - private logger = new Logger(TagLogAdminController.name); - - constructor(private tagLogAdminService: TagLogAdminService) {} - - /** - * 모든 이용자의 월별 누적 출입시간을 반환합니다. - * - * @returns UserAccumulationMonthType[] - */ - @ApiOperation({ - summary: '모든 이용자의 월별 체류시간 조회', - description: '모든 이용자의 월별 체류시간을 조회합니다.', - }) - @ApiResponse({ - status: 200, - type: [UserAccumulationMonthType], - description: '조회 성공', - }) - @ApiResponse({ status: 400, description: '잘못된 날짜 입력' }) - @ApiResponse({ status: 401, description: '접근 권한 없음' }) - @ApiResponse({ - status: 500, - description: '서버 내부 에러 (백앤드 관리자 문의 필요)', - }) - @ApiQuery({ - name: 'year', - description: '년도', - required: true, - }) - @ApiQuery({ - name: 'month', - description: '월', - required: true, - }) - @Get('permonth') - async getPerMonth( - @User() user: UserSessionDto, - @Query('year', ParseIntPipe) year: number, - @Query('month', ParseIntPipe) month: number, - ): Promise { - this.logger.debug(`@getPerMonth) ${year}-${month} by ${user.login}`); - if (!user.is_staff) { - throw new UnauthorizedException({ - description: '관리자 계정으로만 이용 가능한 기능입니다.', - }); - } - const results = await this.tagLogAdminService.getAccumulationInMonthByAll( - year, - month, - ); - return results; - } - - /** - * 특정 이용자의 월별 누적 출입시간을 반환합니다. - * - * @returns UserAccumulationMonthType - */ - @ApiOperation({ - summary: '특정 이용자의 월별 체류시간 조회', - description: '특정 이용자의 월별 체류시간을 조회합니다.', - }) - @ApiResponse({ - status: 200, - type: UserAccumulationMonthType, - description: '조회 성공', - }) - @ApiResponse({ - status: 400, - description: '잘못된 날짜 입력 / 존재하지 않는 login id', - }) - @ApiResponse({ status: 401, description: '접근 권한 없음' }) - @ApiResponse({ - status: 500, - description: '서버 내부 에러 (백앤드 관리자 문의 필요)', - }) - @ApiParam({ - name: 'login', - description: '42 로그인 ID', - required: true, - }) - @ApiQuery({ - name: 'year', - description: '년도', - required: true, - }) - @ApiQuery({ - name: 'month', - description: '월', - required: true, - }) - @Get('permonth/:login') - async getPerMonthByLogin( - @User() user: UserSessionDto, - @Param('login') login: string, - @Query('year', ParseIntPipe) year: number, - @Query('month', ParseIntPipe) month: number, - ): Promise { - this.logger.debug(`@getPerMonthByLogin) ${year}-${month} by ${user.login}`); - if (!user.is_staff) { - throw new UnauthorizedException({ - description: '관리자 계정으로만 이용 가능한 기능입니다.', - }); - } - const id = await this.tagLogAdminService.findIdByLogin(login); - if (id < 0) { - throw new BadRequestException({ - message: '서버상에 존재하지 않는 login ID입니다.', - }); - } - const result = await this.tagLogAdminService.getAccumulationInMonthById( - id, - login, - year, - month, - ); - console.log(result); - return result; - } - - /** - * 모든 이용자의 이용자 별 일별 누적 출입시간을 반환합니다. - * - * @returns UserAccumulationDayType[] - */ - @ApiOperation({ - summary: '모든 이용자의 일별 체류시간 조회', - description: '모든 이용자의 일별 체류시간을 조회합니다.', - }) - @ApiResponse({ - status: 200, - type: [UserAccumulationDayType], - description: '조회 성공', - }) - @ApiResponse({ status: 401, description: '접근 권한 없음' }) - @ApiResponse({ - status: 500, - description: '서버 내부 에러 (백앤드 관리자 문의 필요)', - }) - @ApiQuery({ - name: 'year', - description: '년도', - required: true, - }) - @ApiQuery({ - name: 'month', - description: '월', - required: true, - }) - @Get('perdays') - async getPerDays( - @User() user: UserSessionDto, - @Query('year', ParseIntPipe) year: number, - @Query('month', ParseIntPipe) month: number, - ): Promise { - this.logger.debug(`@getPerDays) ${year}-${month} by ${user.login}`); - if (!user.is_staff) { - throw new UnauthorizedException({ - description: '관리자 계정으로만 이용 가능한 기능입니다.', - }); - } - const results = await this.tagLogAdminService.getPerDaysByAll(year, month); - return results; - } - - /** - * 특정 이용자의 일별 누적 출입시간을 반환합니다. - * - * @returns UserAccumulationDayType - */ - @ApiOperation({ - summary: '특정 이용자의 일별 체류시간 조회', - description: '특정 이용자의 일별 체류시간을 조회합니다.', - }) - @ApiResponse({ - status: 200, - type: UserAccumulationDayType, - description: '조회 성공', - }) - @ApiResponse({ - status: 400, - description: '잘못된 날짜 입력 / 존재하지 않는 login id', - }) - @ApiResponse({ status: 401, description: '접근 권한 없음' }) - @ApiResponse({ - status: 500, - description: '서버 내부 에러 (백앤드 관리자 문의 필요)', - }) - @ApiParam({ - name: 'login', - description: '42 로그인 ID', - required: true, - }) - @ApiQuery({ - name: 'year', - description: '년도', - required: true, - }) - @ApiQuery({ - name: 'month', - description: '월', - required: true, - }) - @Get('perdays/:login') - async getPerDaysByLogin( - @User() user: UserSessionDto, - @Param('login') login: string, - @Query('year', ParseIntPipe) year: number, - @Query('month', ParseIntPipe) month: number, - ): Promise { - this.logger.debug(`@getPerDaysByLogin) ${year}-${month} by ${user.login}`); - if (!user.is_staff) { - throw new UnauthorizedException({ - description: '관리자 계정으로만 이용 가능한 기능입니다.', - }); - } - const id = await this.tagLogAdminService.findIdByLogin(login); - if (id < 0) { - throw new BadRequestException({ - message: '서버상에 존재하지 않는 login ID입니다.', - }); - } - const result = this.tagLogAdminService.getPerDaysById( - id, - login, - year, - month, - ); - return result; - } -} diff --git a/src/tag-log-v2/tag-log-v2-admin.service.ts b/src/tag-log-v2/tag-log-v2-admin.service.ts deleted file mode 100644 index b53fd68..0000000 --- a/src/tag-log-v2/tag-log-v2-admin.service.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { DateCalculator } from 'src/data-calculator/date-calculator.component'; -import { UserService } from 'src/user/user.service'; -import { UserAccumulationDayType } from './dto/admin/user-accumulation-day.type'; -import { UserAccumulationMonthType } from './dto/admin/user-accumulation-month.type'; -import { TagLogService } from './tag-log-v2.service'; - -@Injectable() -export class TagLogAdminService { - private logger = new Logger(TagLogAdminService.name); - - constructor( - private userService: UserService, - private tagLogService: TagLogService, - private dateCalculator: DateCalculator, - ) {} - - genDayType( - id: number, - login: string, - arr: number[], - ): UserAccumulationDayType { - this.logger.debug(`@genDayType) ${id}, ${login} -> ${arr}`); - return { - id, - login, - day_1: arr[0], - day_2: arr[1], - day_3: arr[2], - day_4: arr[3], - day_5: arr[4], - day_6: arr[5], - day_7: arr[6], - day_8: arr[7], - day_9: arr[8], - day_10: arr[9], - day_11: arr[10], - day_12: arr[11], - day_13: arr[12], - day_14: arr[13], - day_15: arr[14], - day_16: arr[15], - day_17: arr[16], - day_18: arr[17], - day_19: arr[18], - day_20: arr[19], - day_21: arr[20], - day_22: arr[21], - day_23: arr[22], - day_24: arr[23], - day_25: arr[24], - day_26: arr[25], - day_27: arr[26], - day_28: arr[27], - day_29: arr[28], - day_30: arr[29], - day_31: arr[30], - }; - } - - async findIdByLogin(login: string): Promise { - this.logger.debug(`@findIdByLogin) ${login}`); - return this.userService.findIdByLogin(login); - } - - /** - * 42 id와 로그인 id 정보를 통해 특정 년월에 클러스터에 체류한 시간을 구합니다. - * - * @param id - * @param login - * @param year - * @param month - * @returns UserAccumulationDayType - */ - async getPerDaysById( - id: number, - login: string, - year: number, - month: number, - ): Promise { - this.logger.debug(`@getPerDaysById) ${id}, ${login}, ${year}, ${month}`); - const date = new Date(`${year}-${month}`); - const resultMonth = await this.tagLogService.getAllTagPerMonth(id, date); - const dayCount = this.dateCalculator.getDaysInMonth(year, month); - const dayArr = Array.from({ length: dayCount }, () => 0); - for (const log of resultMonth) { - const date = new Date(log.inTimeStamp * 1000); - const day = date.getDate(); - dayArr[day - 1] += log.durationSecond; - } - return this.genDayType(id, login, dayArr); - } - - /** - * 42 id와 로그인 id 정보를 통해 특정 년월에 클러스터에 체류한 누적시간을 구합니다. - * - * @param id - * @param login - * @param year - * @param month - * @returns UserAccumulationMonthType - */ - async getAccumulationInMonthById( - id: number, - login: string, - year: number, - month: number, - ): Promise { - this.logger.debug( - `@getAccumulationInMonthById) ${id}, ${login}, ${year}, ${month}`, - ); - const date = new Date(`${year}-${month}`); - const resultMonth = await this.tagLogService.getAllTagPerMonth(id, date); - const monthAccumationTime = resultMonth.reduce( - (prev, result) => result.durationSecond + prev, - 0, - ); - return { - id, - login, - monthAccumationTime, - }; - } - - /** - * DB에 저장된 모든 카뎃의 월의 일별 체류시간을 구합니다. - * - * @param year - * @param month - * @returns UserAccumulationDayType[] - */ - async getPerDaysByAll( - year: number, - month: number, - ): Promise { - this.logger.debug(`@getPerDaysByAll) ${year}, ${month}`); - const cadets = await this.userService.getAllIds(false); - return await Promise.all( - cadets.map((cadet) => - this.getPerDaysById(cadet.user_id, cadet.login, year, month), - ), - ); - } - - /** - * DB에 저장된 모든 카뎃의 월 누적 체류시간을 구합니다. - * - * @param year - * @param month - * @returns UserAccumulationMonthType[] - */ - async getAccumulationInMonthByAll( - year: number, - month: number, - ): Promise { - this.logger.debug(`@getAccumulationInMonthByAll) ${year}, ${month}`); - const cadets = await this.userService.getAllIds(false); - return await Promise.all( - cadets.map((cadet) => - this.getAccumulationInMonthById( - cadet.user_id, - cadet.login, - year, - month, - ), - ), - ); - } -} diff --git a/src/tag-log-v1/dto/UserInOutLogs.type.ts b/src/tag-log/dto/UserInOutLogs.type.ts similarity index 100% rename from src/tag-log-v1/dto/UserInOutLogs.type.ts rename to src/tag-log/dto/UserInOutLogs.type.ts diff --git a/src/tag-log-v1/dto/admin/user-accumulation-day.type.ts b/src/tag-log/dto/admin/user-accumulation-day.type.ts similarity index 100% rename from src/tag-log-v1/dto/admin/user-accumulation-day.type.ts rename to src/tag-log/dto/admin/user-accumulation-day.type.ts diff --git a/src/tag-log-v1/dto/admin/user-accumulation-month-day.type.ts b/src/tag-log/dto/admin/user-accumulation-month-day.type.ts similarity index 100% rename from src/tag-log-v1/dto/admin/user-accumulation-month-day.type.ts rename to src/tag-log/dto/admin/user-accumulation-month-day.type.ts diff --git a/src/tag-log-v1/dto/admin/user-accumulation-month.type.ts b/src/tag-log/dto/admin/user-accumulation-month.type.ts similarity index 100% rename from src/tag-log-v1/dto/admin/user-accumulation-month.type.ts rename to src/tag-log/dto/admin/user-accumulation-month.type.ts diff --git a/src/tag-log-v1/dto/admin/user-id.type.ts b/src/tag-log/dto/admin/user-id.type.ts similarity index 100% rename from src/tag-log-v1/dto/admin/user-id.type.ts rename to src/tag-log/dto/admin/user-id.type.ts diff --git a/src/tag-log-v2/dto/device-info.dto.ts b/src/tag-log/dto/device-info.dto.ts similarity index 100% rename from src/tag-log-v2/dto/device-info.dto.ts rename to src/tag-log/dto/device-info.dto.ts diff --git a/src/tag-log-v1/dto/inout.dto.ts b/src/tag-log/dto/inout.dto.ts similarity index 100% rename from src/tag-log-v1/dto/inout.dto.ts rename to src/tag-log/dto/inout.dto.ts diff --git a/src/tag-log-v1/dto/pair-info.dto.ts b/src/tag-log/dto/pair-info.dto.ts similarity index 100% rename from src/tag-log-v1/dto/pair-info.dto.ts rename to src/tag-log/dto/pair-info.dto.ts diff --git a/src/tag-log-v2/dto/subType/InOutLog.type.ts b/src/tag-log/dto/subType/InOutLog.type.ts similarity index 100% rename from src/tag-log-v2/dto/subType/InOutLog.type.ts rename to src/tag-log/dto/subType/InOutLog.type.ts diff --git a/src/tag-log-v1/dto/subType/InOutLog.type.ts b/src/tag-log/dto/subType/InOutLog.type.v1.ts similarity index 93% rename from src/tag-log-v1/dto/subType/InOutLog.type.ts rename to src/tag-log/dto/subType/InOutLog.type.v1.ts index 5389909..36f1e09 100644 --- a/src/tag-log-v1/dto/subType/InOutLog.type.ts +++ b/src/tag-log/dto/subType/InOutLog.type.v1.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export class InOutLogType { +export class InOutLogTypeV1 { @ApiProperty({ description: '입장한 시간 (타임스탬프, 초 단위)', example: 1658980000, diff --git a/src/tag-log-v1/dto/tag-log.dto.ts b/src/tag-log/dto/tag-log.dto.ts similarity index 100% rename from src/tag-log-v1/dto/tag-log.dto.ts rename to src/tag-log/dto/tag-log.dto.ts diff --git a/src/tag-log-v1/dto/user-Info.type.ts b/src/tag-log/dto/user-Info.type.ts similarity index 100% rename from src/tag-log-v1/dto/user-Info.type.ts rename to src/tag-log/dto/user-Info.type.ts diff --git a/src/tag-log-v2/dto/user-accumulation.type.ts b/src/tag-log/dto/user-accumulation.type.ts similarity index 100% rename from src/tag-log-v2/dto/user-accumulation.type.ts rename to src/tag-log/dto/user-accumulation.type.ts diff --git a/src/tag-log-v1/dto/user-accumulation.type.ts b/src/tag-log/dto/user-accumulation.type.v1.ts similarity index 87% rename from src/tag-log-v1/dto/user-accumulation.type.ts rename to src/tag-log/dto/user-accumulation.type.v1.ts index 121b605..eed01d8 100644 --- a/src/tag-log-v1/dto/user-accumulation.type.ts +++ b/src/tag-log/dto/user-accumulation.type.v1.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -export class UserAccumulationType { +export class UserAccumulationTypeV1 { @ApiProperty({ description: '일별 누적시간', example: 12345, diff --git a/src/tag-log-v2/repository/interface/device-info-repository.interface.ts b/src/tag-log/repository/interface/device-info-repository.interface.ts similarity index 71% rename from src/tag-log-v2/repository/interface/device-info-repository.interface.ts rename to src/tag-log/repository/interface/device-info-repository.interface.ts index b33f727..b1869fe 100644 --- a/src/tag-log-v2/repository/interface/device-info-repository.interface.ts +++ b/src/tag-log/repository/interface/device-info-repository.interface.ts @@ -1,4 +1,4 @@ -import { DeviceInfoDto } from 'src/tag-log-v2/dto/device-info.dto'; +import { DeviceInfoDto } from 'src/tag-log/dto/device-info.dto'; export interface IDeviceInfoRepository { /** diff --git a/src/tag-log-v1/repository/interface/pair-info-repository.interface.ts b/src/tag-log/repository/interface/pair-info-repository.interface.ts similarity index 84% rename from src/tag-log-v1/repository/interface/pair-info-repository.interface.ts rename to src/tag-log/repository/interface/pair-info-repository.interface.ts index 84f68c5..cd377a4 100644 --- a/src/tag-log-v1/repository/interface/pair-info-repository.interface.ts +++ b/src/tag-log/repository/interface/pair-info-repository.interface.ts @@ -1,4 +1,4 @@ -import { PairInfoDto } from 'src/tag-log-v1/dto/pair-info.dto'; +import { PairInfoDto } from 'src/tag-log/dto/pair-info.dto'; export interface IPairInfoRepository { /** diff --git a/src/tag-log-v2/repository/interface/tag-log-repository.interface.ts b/src/tag-log/repository/interface/tag-log-repository.interface.ts similarity index 96% rename from src/tag-log-v2/repository/interface/tag-log-repository.interface.ts rename to src/tag-log/repository/interface/tag-log-repository.interface.ts index 7d0d0a8..68041f3 100644 --- a/src/tag-log-v2/repository/interface/tag-log-repository.interface.ts +++ b/src/tag-log/repository/interface/tag-log-repository.interface.ts @@ -1,4 +1,4 @@ -import { TagLogDto } from 'src/tag-log-v1/dto/tag-log.dto'; +import { TagLogDto } from 'src/tag-log/dto/tag-log.dto'; import { CardDto } from 'src/user/dto/card.dto'; export interface ITagLogRepository { diff --git a/src/tag-log-v2/repository/mysql/device-info.repository.ts b/src/tag-log/repository/mysql/device-info.repository.ts similarity index 88% rename from src/tag-log-v2/repository/mysql/device-info.repository.ts rename to src/tag-log/repository/mysql/device-info.repository.ts index f5649b5..a8fef23 100644 --- a/src/tag-log-v2/repository/mysql/device-info.repository.ts +++ b/src/tag-log/repository/mysql/device-info.repository.ts @@ -1,4 +1,4 @@ -import { DeviceInfoDto } from 'src/tag-log-v2/dto/device-info.dto'; +import { DeviceInfoDto } from 'src/tag-log/dto/device-info.dto'; import { IDeviceInfoRepository } from '../interface/device-info-repository.interface'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; diff --git a/src/tag-log-v1/repository/mysql/pair-info.repository.ts b/src/tag-log/repository/mysql/pair-info.repository.ts similarity index 92% rename from src/tag-log-v1/repository/mysql/pair-info.repository.ts rename to src/tag-log/repository/mysql/pair-info.repository.ts index d6f286b..9665405 100644 --- a/src/tag-log-v1/repository/mysql/pair-info.repository.ts +++ b/src/tag-log/repository/mysql/pair-info.repository.ts @@ -1,8 +1,8 @@ import { InjectRepository } from '@nestjs/typeorm'; -import { PairInfoDto } from 'src/tag-log-v1/dto/pair-info.dto'; -import { IPairInfoRepository } from '../interface/pair-info-repository.interface'; -import { Repository } from 'typeorm'; import { PairInfo } from 'src/entities/pair-info.entity'; +import { PairInfoDto } from 'src/tag-log/dto/pair-info.dto'; +import { Repository } from 'typeorm'; +import { IPairInfoRepository } from '../interface/pair-info-repository.interface'; export class PairInfoRepository implements IPairInfoRepository { constructor( diff --git a/src/tag-log-v1/repository/mysql/tag-log.repository.ts b/src/tag-log/repository/mysql/tag-log.repository.ts similarity index 95% rename from src/tag-log-v1/repository/mysql/tag-log.repository.ts rename to src/tag-log/repository/mysql/tag-log.repository.ts index 1813194..edabdee 100644 --- a/src/tag-log-v1/repository/mysql/tag-log.repository.ts +++ b/src/tag-log/repository/mysql/tag-log.repository.ts @@ -1,9 +1,9 @@ import { InjectRepository } from '@nestjs/typeorm'; -import { TagLogDto } from 'src/tag-log-v1/dto/tag-log.dto'; -import { ITagLogRepository } from '../interface/tag-log-repository.interface'; -import { Repository, In, Between, LessThan, MoreThan } from 'typeorm'; import { TagLog } from 'src/entities/tag-log.entity'; +import { TagLogDto } from 'src/tag-log/dto/tag-log.dto'; import { CardDto } from 'src/user/dto/card.dto'; +import { Between, In, LessThan, MoreThan, Repository } from 'typeorm'; +import { ITagLogRepository } from '../interface/tag-log-repository.interface'; export class TagLogRepository implements ITagLogRepository { constructor( diff --git a/src/tag-log-v1/tag-log-admin.controller.ts b/src/tag-log/tag-log-admin.controller.ts similarity index 98% rename from src/tag-log-v1/tag-log-admin.controller.ts rename to src/tag-log/tag-log-admin.controller.ts index ec30c22..37c3110 100644 --- a/src/tag-log-v1/tag-log-admin.controller.ts +++ b/src/tag-log/tag-log-admin.controller.ts @@ -17,20 +17,20 @@ import { ApiResponse, ApiTags, } from '@nestjs/swagger'; -import { User } from 'src/auth/user.decorator'; import { UserSessionDto } from 'src/auth/dto/user.session.dto'; +import { AdminAuthGuard } from 'src/auth/guard/admin-auth.guard'; +import { User } from 'src/auth/user.decorator'; import { UserAccumulationDayType } from './dto/admin/user-accumulation-day.type'; import { UserAccumulationMonthType } from './dto/admin/user-accumulation-month.type'; import { TagLogAdminService } from './tag-log-admin.service'; -import { AdminAuthGuard } from 'src/auth/guard/admin-auth.guard'; -@ApiTags('체류 시간 산출 (관리자 전용 API)') -@ApiBearerAuth() -@UseGuards(AdminAuthGuard) +@ApiTags('체류 시간 산출 v2 (관리자 전용 API)') @Controller({ - version: '1', + version: '2', path: 'tag-log/admin', }) +@ApiBearerAuth() +@UseGuards(AdminAuthGuard) export class TagLogAdminController { private logger = new Logger(TagLogAdminController.name); @@ -73,15 +73,18 @@ export class TagLogAdminController { @Query('month', ParseIntPipe) month: number, ): Promise { this.logger.debug(`@getPerMonth) ${year}-${month} by ${user.login}`); + if (!user.is_staff) { throw new UnauthorizedException({ description: '관리자 계정으로만 이용 가능한 기능입니다.', }); } + const results = await this.tagLogAdminService.getAccumulationInMonthByAll( year, month, ); + return results; } @@ -131,23 +134,28 @@ export class TagLogAdminController { @Query('month', ParseIntPipe) month: number, ): Promise { this.logger.debug(`@getPerMonthByLogin) ${year}-${month} by ${user.login}`); + if (!user.is_staff) { throw new UnauthorizedException({ description: '관리자 계정으로만 이용 가능한 기능입니다.', }); } + const id = await this.tagLogAdminService.findIdByLogin(login); + if (id < 0) { throw new BadRequestException({ message: '서버상에 존재하지 않는 login ID입니다.', }); } + const result = await this.tagLogAdminService.getAccumulationInMonthById( id, login, year, month, ); + return result; } @@ -187,12 +195,15 @@ export class TagLogAdminController { @Query('month', ParseIntPipe) month: number, ): Promise { this.logger.debug(`@getPerDays) ${year}-${month} by ${user.login}`); + if (!user.is_staff) { throw new UnauthorizedException({ description: '관리자 계정으로만 이용 가능한 기능입니다.', }); } + const results = await this.tagLogAdminService.getPerDaysByAll(year, month); + return results; } @@ -242,23 +253,28 @@ export class TagLogAdminController { @Query('month', ParseIntPipe) month: number, ): Promise { this.logger.debug(`@getPerDaysByLogin) ${year}-${month} by ${user.login}`); + if (!user.is_staff) { throw new UnauthorizedException({ description: '관리자 계정으로만 이용 가능한 기능입니다.', }); } + const id = await this.tagLogAdminService.findIdByLogin(login); + if (id < 0) { throw new BadRequestException({ message: '서버상에 존재하지 않는 login ID입니다.', }); } + const result = this.tagLogAdminService.getPerDaysById( id, login, year, month, ); + return result; } } diff --git a/src/tag-log-v1/tag-log-admin.service.ts b/src/tag-log/tag-log-admin.service.ts similarity index 96% rename from src/tag-log-v1/tag-log-admin.service.ts rename to src/tag-log/tag-log-admin.service.ts index 00818f0..b40fe1f 100644 --- a/src/tag-log-v1/tag-log-admin.service.ts +++ b/src/tag-log/tag-log-admin.service.ts @@ -21,6 +21,7 @@ export class TagLogAdminService { arr: number[], ): UserAccumulationDayType { this.logger.debug(`@genDayType) ${id}, ${login} -> ${arr}`); + return { id, login, @@ -60,6 +61,7 @@ export class TagLogAdminService { async findIdByLogin(login: string): Promise { this.logger.debug(`@findIdByLogin) ${login}`); + return this.userService.findIdByLogin(login); } @@ -79,15 +81,21 @@ export class TagLogAdminService { month: number, ): Promise { this.logger.debug(`@getPerDaysById) ${id}, ${login}, ${year}, ${month}`); + const date = new Date(`${year}-${month}`); - const resultMonth = await this.tagLogService.getPerMonth(id, date); + + const resultMonth = await this.tagLogService.getAllTagPerMonth(id, date); + const dayCount = this.dateCalculator.getDaysInMonth(year, month); + const dayArr = Array.from({ length: dayCount }, () => 0); + for (const log of resultMonth) { const date = new Date(log.inTimeStamp * 1000); const day = date.getDate(); dayArr[day - 1] += log.durationSecond; } + return this.genDayType(id, login, dayArr); } @@ -110,7 +118,7 @@ export class TagLogAdminService { `@getAccumulationInMonthById) ${id}, ${login}, ${year}, ${month}`, ); const date = new Date(`${year}-${month}`); - const resultMonth = await this.tagLogService.getPerMonth(id, date); + const resultMonth = await this.tagLogService.getAllTagPerMonth(id, date); const monthAccumationTime = resultMonth.reduce( (prev, result) => result.durationSecond + prev, 0, diff --git a/src/tag-log-v2/tag-log-v2.controller.ts b/src/tag-log/tag-log.controller.ts similarity index 99% rename from src/tag-log-v2/tag-log-v2.controller.ts rename to src/tag-log/tag-log.controller.ts index 4173ac5..bba8780 100644 --- a/src/tag-log-v2/tag-log-v2.controller.ts +++ b/src/tag-log/tag-log.controller.ts @@ -19,7 +19,7 @@ import { User } from 'src/auth/user.decorator'; import { UserInOutLogsType } from './dto/UserInOutLogs.type'; import { UserInfoType } from './dto/user-Info.type'; import { UserAccumulationType } from './dto/user-accumulation.type'; -import { TagLogService } from './tag-log-v2.service'; +import { TagLogService } from './tag-log.service'; @ApiTags('체류 시간 산출 v2') @Controller({ diff --git a/src/tag-log-v2/tag-log-v2.module.ts b/src/tag-log/tag-log.module.ts similarity index 84% rename from src/tag-log-v2/tag-log-v2.module.ts rename to src/tag-log/tag-log.module.ts index 42c1bc7..3cd3e2f 100644 --- a/src/tag-log-v2/tag-log-v2.module.ts +++ b/src/tag-log/tag-log.module.ts @@ -9,10 +9,10 @@ import { UserModule } from 'src/user/user.module'; import { DeviceInfoRepository } from './repository/mysql/device-info.repository'; import { PairInfoRepository } from './repository/mysql/pair-info.repository'; import { TagLogRepository } from './repository/mysql/tag-log.repository'; -import { TagLogAdminController } from './tag-log-v2-admin.controller'; -import { TagLogAdminService } from './tag-log-v2-admin.service'; -import { TagLogController } from './tag-log-v2.controller'; -import { TagLogService } from './tag-log-v2.service'; +import { TagLogAdminController } from './tag-log-admin.controller'; +import { TagLogAdminService } from './tag-log-admin.service'; +import { TagLogService } from './tag-log.service'; +import { TagLogController } from './tag-log.controller'; const tagLogRepo = { provide: 'ITagLogRepository', diff --git a/src/tag-log-v2/tag-log-v2.service.spec.ts b/src/tag-log/tag-log.service.spec.ts similarity index 97% rename from src/tag-log-v2/tag-log-v2.service.spec.ts rename to src/tag-log/tag-log.service.spec.ts index a3e661f..9de3943 100644 --- a/src/tag-log-v2/tag-log-v2.service.spec.ts +++ b/src/tag-log/tag-log.service.spec.ts @@ -1,15 +1,20 @@ import { Test, TestingModule } from '@nestjs/testing'; import { DateCalculator } from 'src/data-calculator/date-calculator.component'; +import { StatisticsService } from 'src/statistics/statictics.service'; import { UserService } from 'src/user/user.service'; import { PairInfoDto } from './dto/pair-info.dto'; import { TagLogDto } from './dto/tag-log.dto'; -import { TagLogService } from './tag-log-v2.service'; +import { TagLogService } from './tag-log.service'; const mocks = [ { provide: UserService, useClass: jest.fn(), }, + { + provide: StatisticsService, + useClass: jest.fn(), + }, { provide: 'ITagLogRepository', useClass: jest.fn(), diff --git a/src/tag-log-v2/tag-log-v2.service.ts b/src/tag-log/tag-log.service.ts similarity index 66% rename from src/tag-log-v2/tag-log-v2.service.ts rename to src/tag-log/tag-log.service.ts index c042ea5..ccae69e 100644 --- a/src/tag-log-v2/tag-log-v2.service.ts +++ b/src/tag-log/tag-log.service.ts @@ -8,6 +8,7 @@ import { DeviceInfoDto } from './dto/device-info.dto'; import { InOutDto } from './dto/inout.dto'; import { PairInfoDto } from './dto/pair-info.dto'; import { InOutLogType } from './dto/subType/InOutLog.type'; +import { InOutLogTypeV1 } from './dto/subType/InOutLog.type.v1'; import { TagLogDto } from './dto/tag-log.dto'; import { IDeviceInfoRepository } from './repository/interface/device-info-repository.interface'; import { IPairInfoRepository } from './repository/interface/pair-info-repository.interface'; @@ -18,17 +19,23 @@ export class TagLogService { private logger = new Logger(TagLogService.name); constructor( - private statisticsService: StatisticsService, - private userService: UserService, @Inject('ITagLogRepository') private tagLogRepository: ITagLogRepository, @Inject('IPairInfoRepository') private pairInfoRepository: IPairInfoRepository, @Inject('IDeviceInfoRepository') private deviceInfoRepository: IDeviceInfoRepository, + private statisticsService: StatisticsService, + private userService: UserService, private dateCalculator: DateCalculator, ) {} + /** + * + * @param day n일 이상 체류중인 카드는 제외합니다. + * @returns + * @version 4 + */ async getCadetPerCluster(day: number) { return await this.statisticsService.getCadetPerCluster(day); } @@ -39,6 +46,7 @@ export class TagLogService { * * @param taglogs TagLogDto[] * @return TagLogDto[] + * @version 4 */ removeDuplicates(taglogs: TagLogDto[]): TagLogDto[] { const comp = (a: TagLogDto, b: TagLogDto) => @@ -58,7 +66,7 @@ export class TagLogService { * @param cards CardDto[] 태깅한 유저의 카드 정보와 유효 기간 * @param deviceInfos DeviceInfoDto[] 디바이스 정보 * @return TagLogDto[] 태그 로그 - * @version 1 + * @version 4 */ async checkAndInsertStartVirtualTagLog( taglogs: TagLogDto[], @@ -108,7 +116,7 @@ export class TagLogService { * @param cards CardDto[] 태깅한 유저의 카드 정보와 유효 기간 * @param deviceInfos DeviceInfoDto[] 디바이스 정보 * @return TagLogDto[] 태그 로그 - * @version 1 + * @version 4 */ async checkAndInsertEndVirtualTagLog( taglogs: TagLogDto[], @@ -179,13 +187,14 @@ export class TagLogService { return taglogs; } - ///** - // * 인자로 들어간 디바이스가 쌍이 맞는지 확인하는 함수입니다. - // * - // * @param deviceInfo - // * @param enterDevice - // * @param device - // */ + /** + * 인자로 들어간 디바이스가 쌍이 맞는지 확인하는 함수입니다. + * + * @param deviceInfo + * @param enterDevice + * @param device + * @version 2 + */ validateDevicePair( deviceInfos: PairInfoDto[], inDevice: number, @@ -205,6 +214,7 @@ export class TagLogService { * * @param deviceInfos * @param targetDevice + * @version 4 */ isInDevice(deviceInfos: PairInfoDto[], targetDevice: number): boolean { const inDevice = deviceInfos.find( @@ -219,6 +229,7 @@ export class TagLogService { * * @param deviceInfos * @param targetDevice + * @version 4 */ isOutDevice(deviceInfos: PairInfoDto[], targetDevice: number): boolean { const outDevice = deviceInfos.find( @@ -234,6 +245,7 @@ export class TagLogService { * * @param taglogs 입출입 로그 * @param deviceInfos 기기 짝 정보 + * @version 4 */ getAllPairsByTagLogs( taglogs: TagLogDto[], @@ -358,6 +370,7 @@ export class TagLogService { * @param tagStart Date * @param tagEnd Date * @returns TagLogDto[] + * @version 4 */ async getAllTagLogsByPeriod( userId: number, @@ -410,6 +423,7 @@ export class TagLogService { * @param userId 사용자 ID * @param date 날짜 * @returns InOutLogType[] + * @version 4 */ async getAllTagPerDay(userId: number, date: Date): Promise { this.logger.debug(`@getAllTagPerDay) ${userId}, ${date}`); @@ -434,6 +448,7 @@ export class TagLogService { * * @param userId 사용자 ID * @returns number[] + * @version 4 */ async getTimeSixWeek(userId: number): Promise { const today = new Date(); @@ -484,6 +499,7 @@ export class TagLogService { * @param userId 사용자 ID * @param date 날짜 * @returns InOutLogType[] + * @version 4 */ async getAllTagPerMonth(userId: number, date: Date): Promise { this.logger.debug(`@getTagPerMonth) ${userId}, ${date}`); @@ -505,6 +521,7 @@ export class TagLogService { * * @param userId 사용자 ID * @returns number[] + * @version 4 */ async getTimeSixMonth(userId: number): Promise { this.logger.debug(`@getTagPerMonth) by ${userId}`); @@ -551,6 +568,7 @@ export class TagLogService { * * @param userId 사용자 ID * @returns InOutDto + * @version 2 */ async checkClusterById(userId: number): Promise { this.logger.debug(`@checkClusterById) ${userId}`); @@ -579,4 +597,257 @@ export class TagLogService { inout: inout, }; } + /** + * 카드 태그 로그에 대해 로그 맨 앞, 맨 뒤 원소가 잘려 있다면 앞, 뒤에 가상의 입출입 로그를 삽입합니다. + * 삽입하는 로그는 짝 여부에 관계없이 전후 원소를 삽입합니다. + * 짝 일치 여부 판단은 다른 로직에서 진행합니다. + * version 2: 카드의 짝을 맞추도록 수정하였습니다. + * version 3: 이전/이후 로그를 가져올 때, 카드의 유효 기간도 고려하도록 변경하였습니다. + * + * @param taglogs TagLogDto[] + * @return TagLogDto[] + * @version 3 + */ + async trimTagLogs( + taglogs: TagLogDto[], + cards: CardDto[], + start: Date, + end: Date, + ): Promise { + this.logger.debug(`@trimTagLogs)`); + // 1. 맨 앞의 로그를 가져옴. + const firstLog = taglogs.at(0); + if (firstLog) { + // 2. 맨 앞의 로그 이전의 로그를 가져옴. + const beforeFirstLog = await this.tagLogRepository.findPrevTagLog( + cards.find((v) => v.card_id === firstLog.card_id), + firstLog.tag_at, + ); + // NOTE: tag log에 기록된 첫번째 로그가 퇴실인 경우 현재는 짝을 맞추지 않음. + if (beforeFirstLog !== null) { + const virtualEnterTime = this.dateCalculator.getStartOfDate(start); + taglogs.unshift({ + tag_at: virtualEnterTime, + device_id: beforeFirstLog.device_id, + idx: -1, + card_id: beforeFirstLog.card_id, + }); + } + } + // 3. 맨 뒤의 로그를 가져옴. + const lastLog = taglogs.at(-1); + if (lastLog) { + // 6. 맨 뒤의 로그 이후의 로그를 가져옴. + const afterLastLog = await this.tagLogRepository.findNextTagLog( + cards.find((v) => v.card_id === lastLog.card_id), + lastLog.tag_at, + ); + // NOTE: 현재는 카뎃의 현재 입실여부에 관계없이 짝을 맞춤. + if (afterLastLog !== null) { + const virtualLeaveTime = this.dateCalculator.getEndOfDate(end); + taglogs.push({ + tag_at: virtualLeaveTime, + device_id: afterLastLog.device_id, + idx: -1, + card_id: afterLastLog.card_id, + }); + } + } + return taglogs; + } + + /** + * 기기 쌍 정보와 태깅한 로그들을 이용하여 출입 로그를 반환합니다. + * + * @param taglogs 입출입 로그 + * @param deviceInfos 기기 짝 정보 + * @version 2 + */ + getPairsByTagLogs( + taglogs: TagLogDto[], + deviceInfos: PairInfoDto[], + ): InOutLogTypeV1[] { + this.logger.debug(`@getPairsByTagLogs)`); + + /** + * 데이터를 날짜의 내림차순으로 정렬 + */ + // const timeLines = taglogs + // //.sort((a, b) => (a.tag_at < b.tag_at ? 1 : -1)) + // .map((v) => ({ + // tag_at: v.tag_at, + // device_id: v.device_id, + // })); + const timeLines = taglogs; + + let leave: TagLogDto | null = null; + const resultPairs: InOutLogTypeV1[] = []; + + // 타임라인 배열이 빌 때까지 루프를 돌립니다. + while (timeLines.length > 0) { + // 가장 뒤의 원소(가장 늦은 날짜)를 뽑습니다. + const val = timeLines.pop(); + + // 만약 퇴장로그로 가정한 로그가 없다면 뽑은 원소를 넣고 루프를 다시 실행합니다. + if (leave === null) { + leave = val; + continue; + } + + // 뽑아낸 원소를 임시로 저장합니다. + const temp: TagLogDto | null = val; + + // 퇴장로그로 가정한 로그와 현재 뽑아낸 로그가 기기 짝이 맞는지 확인합니다. + if ( + this.validateDevicePair(deviceInfos, temp.device_id, leave.device_id) + ) { + // 기기 짝이 맞을 경우 임시 원소를 입장 로그로 지정합니다. + const enter = temp; + + // 입장 로그와 퇴장 로그가 짝이 맞는지 확인합니다. + if (this.dateCalculator.checkEqualDay(enter.tag_at, leave.tag_at)) { + // 만약 동일한 날짜에 속한다면 해당 쌍이 짝이 됩니다. + const inTimeStamp = this.dateCalculator.toTimestamp(enter.tag_at); + const outTimeStamp = this.dateCalculator.toTimestamp(leave.tag_at); + resultPairs.push({ + inTimeStamp, + outTimeStamp, + durationSecond: outTimeStamp - inTimeStamp, + }); + } else { + // 만약 입장 로그와 퇴장 로그가 짝이 맞지만, 동일한 날짜에 속하지 않으면 일 단위로 자릅니다. + + // 퇴장 로그의 정시 (00시)를 기준으로 두 날짜의 간격을 자릅니다. 두 날짜는 가상의 입퇴장 짝이 됩니다. + const virtualEnterTime = this.dateCalculator.getStartOfDate( + leave.tag_at, + ); + const virtualLeaveTime = this.dateCalculator.getEndOfLastDate( + leave.tag_at, + ); + + // 가상 입장시간 (퇴장시간의 날짜의 정시 - 00시)과 퇴장시간이 짝을 맞춥니다. + const inTimeStamp = this.dateCalculator.toTimestamp(virtualEnterTime); + const outTimeStamp = this.dateCalculator.toTimestamp(leave.tag_at); + resultPairs.push({ + inTimeStamp, + outTimeStamp, + durationSecond: outTimeStamp - inTimeStamp, + }); + + // 그리고 가상 퇴장시간을 다시 배열에 넣어 가상 퇴장시간과 맞는 짝을 찾습니다. + timeLines.push(val); + timeLines.push({ + tag_at: virtualLeaveTime, + device_id: leave.device_id, + idx: -1, + card_id: val.card_id, + }); + } + + // 퇴장로그로 가정한 로그를 초기화합니다. + leave = null; + } else { + // pair가 아닌 경우 뽑아낸 로그를 퇴장로그로 가정하고 루프를 다시 실행합니다. + leave = temp; + } + } + + return resultPairs; + } + + /** + * 인자로 들어가는 사용자 ID와 날짜에 대한 일별 누적시간을 반환합니다. + * + * @param userId 사용자 ID + * @param date 날짜 + * @returns InOutLogType[] + * @version 2 + */ + async getPerDay(userId: number, date: Date): Promise { + this.logger.debug(`@getPerDay) ${userId}, ${date}`); + const tagStart = this.dateCalculator.getStartOfDate(date); + const tagEnd = this.dateCalculator.getEndOfDate(date); + + const pairs = await this.pairInfoRepository.findAll(); + + const cards = await this.userService.findCardsByUserId( + userId, + tagStart, + tagEnd, + ); + + const tagLogs = await this.tagLogRepository.findTagLogsByCards( + cards, + tagStart, + tagEnd, + ); + + const sortedTagLogs = tagLogs.sort((a, b) => + a.tag_at > b.tag_at ? 1 : -1, + ); + + // FIXME: 임시 조치임 + const filteredTagLogs = sortedTagLogs.filter( + (v) => v.device_id !== 35 && v.device_id !== 16, + ); + + const trimmedTagLogs = await this.trimTagLogs( + filteredTagLogs, + cards, + tagStart, + tagEnd, + ); + + const resultPairs = this.getPairsByTagLogs(trimmedTagLogs, pairs); + + return resultPairs; + } + + /** + * 인자로 들어가는 사용자 ID와 날짜에 대한 월별 누적시간을 반환합니다. + * + * @param userId 사용자 ID + * @param date 날짜 + * @returns InOutLogType[] + * @version 2 + */ + async getPerMonth(userId: number, date: Date): Promise { + this.logger.debug(`@getPerMonth) ${userId}, ${date}`); + const tagStart = this.dateCalculator.getStartOfMonth(date); + const tagEnd = this.dateCalculator.getEndOfMonth(date); + + const pairs = await this.pairInfoRepository.findAll(); + + const cards = await this.userService.findCardsByUserId( + userId, + tagStart, + tagEnd, + ); + + const tagLogs = await this.tagLogRepository.findTagLogsByCards( + cards, + tagStart, + tagEnd, + ); + + const sortedTagLogs = tagLogs.sort((a, b) => + a.tag_at > b.tag_at ? 1 : -1, + ); + + // FIXME: 임시 조치임 + const filteredTagLogs = sortedTagLogs.filter( + (v) => v.device_id !== 35 && v.device_id !== 16, + ); + + const trimmedTagLogs = await this.trimTagLogs( + filteredTagLogs, + cards, + tagStart, + tagEnd, + ); + + const resultPairs = this.getPairsByTagLogs(trimmedTagLogs, pairs); + + return resultPairs; + } }