From 93451bb497a68c7139c384c6266541167269cc74 Mon Sep 17 00:00:00 2001 From: Manta <32708404+Soecka@users.noreply.github.com> Date: Tue, 7 Jan 2025 22:21:58 +0800 Subject: [PATCH] [enhance] CheckEvent controller with new query endpoints (#32) --- ReadMe.md | 2 +- src/controller/CheckEvent.ts | 62 +++++++++++++++++- src/model/CheckEvent.ts | 121 ++++++++++++++++++++++++++++++++++- src/model/index.ts | 19 +++++- 4 files changed, 198 insertions(+), 6 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 0e1c351..c2b12d3 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -127,7 +127,7 @@ git push origin master --tags [7]: https://typeorm.io/ [8]: https://swagger.io/ [9]: https://github.com/anttiviljami/openapi-backend -[10]: https://github.com/settings/tokens +[10]: https://github.com/settings/tokens/new?description=KYS-service&scopes=read:packages [11]: https://www.postgresql.org/ [12]: https://azure.microsoft.com/en-us/products/storage/blobs [13]: https://www.leancloud.cn/ diff --git a/src/controller/CheckEvent.ts b/src/controller/CheckEvent.ts index 5e55bd4..3da0ca1 100644 --- a/src/controller/CheckEvent.ts +++ b/src/controller/CheckEvent.ts @@ -6,25 +6,41 @@ import { ForbiddenError, Get, JsonController, + Param, Post, QueryParams } from 'routing-controllers'; import { ResponseSchema } from 'routing-controllers-openapi'; import { + ActivityAgendaCheckInListChunk, + ActivityAgendaCheckInSummary, + ActivityCheckInListChunk, + ActivityCheckInSummary, + BaseFilter, CheckEvent, CheckEventChunk, CheckEventFilter, CheckEventInput, + dataSource, User, - dataSource + UserActivityCheckInListChunk, + UserActivityCheckInSummary } from '../model'; import { ActivityLogController } from './ActivityLog'; +import { FindOptionsWhere } from 'typeorm'; @JsonController('/event/check') export class CheckEventController { store = dataSource.getRepository(CheckEvent); userStore = dataSource.getRepository(User); + userActivityCheckInStore = dataSource.getRepository( + UserActivityCheckInSummary + ); + activityCheckInStore = dataSource.getRepository(ActivityCheckInSummary); + activityAgendaCheckInStore = dataSource.getRepository( + ActivityAgendaCheckInSummary + ); @Post() @Authorized() @@ -79,4 +95,48 @@ export class CheckEventController { }); return { list, count }; } + + @Get('/user/:id') + @ResponseSchema(UserActivityCheckInListChunk) + async getCheckEventList( + @Param('id') id: number, + @QueryParams() { pageSize = 10, pageIndex = 1 }: BaseFilter + ) { + const [list, count] = await this.userActivityCheckInStore.findAndCount({ + where: { userId: id }, + skip: pageSize * (pageIndex - 1), + take: pageSize + }); + + for (const item of list) + item.user = await this.userStore.findOneBy({ id: item.userId }); + return { list, count }; + } + + @Get('/activity/:id') + @ResponseSchema(ActivityAgendaCheckInListChunk) + async getActivityCheckEventList( + @Param('id') id: string, + @QueryParams() { pageSize = 10, pageIndex = 1 }: BaseFilter + ) { + const [list, count] = + await this.activityAgendaCheckInStore.findAndCount({ + where: { activityId: id }, + skip: pageSize * (pageIndex - 1), + take: pageSize + }); + return { list, count }; + } + + @Get('/activity') + @ResponseSchema(ActivityCheckInListChunk) + async getAgendaCheckEventList( + @QueryParams() { pageSize = 10, pageIndex = 1 }: BaseFilter + ) { + const [list, count] = await this.activityCheckInStore.findAndCount({ + skip: pageSize * (pageIndex - 1), + take: pageSize + }); + return { list, count }; + } } diff --git a/src/model/CheckEvent.ts b/src/model/CheckEvent.ts index 9782efa..0854d04 100644 --- a/src/model/CheckEvent.ts +++ b/src/model/CheckEvent.ts @@ -1,5 +1,6 @@ import { Type } from 'class-transformer'; import { + IsEnum, IsInt, IsLatLong, IsOptional, @@ -7,7 +8,7 @@ import { Min, ValidateNested } from 'class-validator'; -import { Column, Entity, ManyToOne } from 'typeorm'; +import { Column, Entity, ManyToOne, ViewColumn, ViewEntity } from 'typeorm'; import { BaseFilter, ListChunk } from './Base'; import { User, UserBase, UserInputData } from './User'; @@ -90,3 +91,121 @@ export class CheckEventChunk implements ListChunk { @ValidateNested({ each: true }) list: CheckEvent[]; } + +@ViewEntity({ + expression: connection => + connection + .createQueryBuilder() + .from(CheckEvent, 'ce') + .groupBy('ce.user.id, ce.activityId') + .select('ce.activityId', 'activityId') + .addSelect('ce.user.id', 'userId') + .addSelect('ce.activityName', 'activityName') + .addSelect('COUNT(ce.id)', 'checkCount') +}) +export class UserActivityCheckInSummary { + @ViewColumn() + @IsInt() + @Min(1) + userId: number; + + @ViewColumn() + @IsString() + activityId: string; + + @ViewColumn() + @IsString() + activityName: string; + + @ViewColumn() + @IsInt() + @Min(0) + checkCount: number; + + @Type(() => User) + @ValidateNested() + user: User; +} + +@ViewEntity({ + expression: connection => + connection + .createQueryBuilder() + .from(CheckEvent, 'ce') + .groupBy('ce.activityId') + .select('ce.activityId', 'activityId') + .addSelect('ce.activityName', 'activityName') + .addSelect('COUNT(ce.id)', 'checkCount') +}) +export class ActivityCheckInSummary { + @ViewColumn() + @IsString() + activityId: string; + + @ViewColumn() + @IsString() + activityName: string; + + @ViewColumn() + @IsInt() + @Min(0) + checkCount: number; +} + +@ViewEntity({ + expression: connection => + connection + .createQueryBuilder() + .from(CheckEvent, 'ce') + .groupBy('ce.activityId, ce.agendaId') + .select('ce.activityId', 'activityId') + .addSelect('ce.activityName', 'activityName') + .addSelect('ce.agendaId', 'agendaId') + .addSelect('ce.agendaTitle', 'agendaTitle') + .addSelect('COUNT(ce.id)', 'checkCount') +}) +export class ActivityAgendaCheckInSummary extends ActivityCheckInSummary { + @ViewColumn() + @IsString() + agendaId: string; + + @ViewColumn() + @IsString() + agendaTitle: string; +} + +export class UserActivityCheckInListChunk + implements ListChunk +{ + @IsInt() + @Min(0) + count: number; + + @Type(() => UserActivityCheckInSummary) + @ValidateNested({ each: true }) + list: UserActivityCheckInSummary[]; +} + +export class ActivityCheckInListChunk + implements ListChunk +{ + @IsInt() + @Min(0) + count: number; + + @Type(() => ActivityCheckInSummary) + @ValidateNested({ each: true }) + list: ActivityCheckInSummary[]; +} + +export class ActivityAgendaCheckInListChunk + implements ListChunk +{ + @IsInt() + @Min(0) + count: number; + + @Type(() => ActivityAgendaCheckInSummary) + @ValidateNested({ each: true }) + list: ActivityAgendaCheckInSummary[]; +} diff --git a/src/model/index.ts b/src/model/index.ts index 38d9189..f340579 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -3,9 +3,14 @@ import { DataSource } from 'typeorm'; import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'; import { DATABASE_URL, isProduct } from '../utility'; -import { CheckEvent } from './CheckEvent'; +import { + ActivityAgendaCheckInSummary, + ActivityCheckInSummary, + CheckEvent, + UserActivityCheckInSummary +} from './CheckEvent'; import { User } from './User'; -import { ActivityLog } from './ActivityLog'; +import { ActivityLog, UserRank } from './ActivityLog'; export * from './Base'; export * from './CheckEvent'; @@ -24,7 +29,15 @@ const commonOptions: Pick< > = { logging: true, synchronize: true, - entities: [User, ActivityLog, CheckEvent], + entities: [ + User, + UserRank, + ActivityLog, + CheckEvent, + UserActivityCheckInSummary, + ActivityAgendaCheckInSummary, + ActivityCheckInSummary + ], migrations: [`${isProduct ? '.tmp' : 'migration'}/*.ts`] };