Skip to content

Commit

Permalink
feat: 회원 목록 조회 API 구현 (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
Coalery authored Dec 28, 2024
2 parents ee82525 + d0b2bd3 commit d0d0f1c
Show file tree
Hide file tree
Showing 23 changed files with 398 additions and 128 deletions.
2 changes: 1 addition & 1 deletion src/__test__/fixtures/CacheFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {

function generator(params: Partial<CacheConstructorParams> = {}): Cache {
return new Cache({
id: params.id ?? faker.string.numeric(3),
id: params.id ?? faker.number.int(),
name: params.name ?? faker.string.alpha(10),
content: params.content ?? faker.string.alpha(10),
updatedAt: params.updatedAt ?? new Date(),
Expand Down
2 changes: 1 addition & 1 deletion src/__test__/fixtures/domain/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function generateUser(params?: Partial<UserConstructorParams>): User {
email: faker.internet.email(),
phone: faker.phone.number(),
homepage: faker.internet.url(),
languages: [faker.lorem.word()],
language: faker.lorem.word(),
prefer: faker.lorem.word(),
}),
admission: faker.lorem.word(),
Expand Down
2 changes: 1 addition & 1 deletion src/__test__/fixtures/view/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function generateGroupMemberView(
groupId: faker.string.uuid(),
memberId: faker.string.uuid(),
memberName: faker.lorem.word(),
languages: [faker.lorem.word()],
language: faker.lorem.word(),
interests: [
{
id: faker.string.uuid(),
Expand Down
2 changes: 1 addition & 1 deletion src/__test__/fixtures/view/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function generateUserView(params?: Partial<UserView>): UserView {
email: faker.internet.email(),
phone: faker.phone.number(),
homepage: faker.internet.url(),
languages: [faker.lorem.word()],
language: faker.lorem.word(),
prefer: faker.lorem.word(),
},
admission: faker.lorem.word(),
Expand Down
3 changes: 2 additions & 1 deletion src/app/AppModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { MikroOrmModule } from '@mikro-orm/nestjs';
import { Module } from '@nestjs/common';

import { InfraBlueManageController } from '@khlug/app/interface/infraBlue/manager/InfraBlueManageController';
import { UserManageController } from '@khlug/app/interface/user/manager/UserManageController';

import { UpdateDoorLockPasswordCommandHandler } from '@khlug/app/application/infraBlue/command/updateDoorLockPassword/UpdateDoorLockPasswordCommandHandler';
import { GetDoorLockPasswordQueryHandler } from '@khlug/app/application/infraBlue/query/getDoorLockPassword/GetDoorLockPasswordQueryHandler';

import { Cache } from '@khlug/app/domain/cache/model/Cache';

const controllers = [];
const manageControllers = [InfraBlueManageController];
const manageControllers = [InfraBlueManageController, UserManageController];

const commandHandlers = [UpdateDoorLockPasswordCommandHandler];
const queryHandlers = [GetDoorLockPasswordQueryHandler];
Expand Down
2 changes: 1 addition & 1 deletion src/app/application/group/query/view/GroupMemberView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export interface GroupMemberView {
groupId: string;
memberId: string;
memberName: string;
languages: string[] | null;
language: string | null;
interests: {
id: string;
name: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export class UpdateUserCommand implements ICommand {
readonly email: string,
readonly phone: string | null,
readonly homepage: string,
readonly languages: string[],
readonly language: string,
readonly interestIds: string[],
readonly prefer: string,
) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('UpdateUserCommandHandler', () => {
const email = '[email protected]';
const phone = null;
const homepage = 'https://some.home.page/';
const languages = ['some', 'languages'];
const language = 'some, language';
const interestIds = ['interest1', 'interest2'];
const prefer = 'some-prefers';

Expand All @@ -95,7 +95,7 @@ describe('UpdateUserCommandHandler', () => {
email,
phone,
homepage,
languages,
language,
interestIds,
prefer,
);
Expand Down Expand Up @@ -140,7 +140,7 @@ describe('UpdateUserCommandHandler', () => {
email,
phone,
homepage,
languages,
language,
prefer,
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ export class UpdateUserCommandHandler

@Transactional()
async execute(command: UpdateUserCommand): Promise<UpdateUserCommandResult> {
const { userId, email, phone, homepage, languages, interestIds, prefer } =
const { userId, email, phone, homepage, language, interestIds, prefer } =
command;

const user = await this.userRepository.findById(userId);
if (!user) {
throw new NotFoundException(Message.USER_NOT_FOUND);
}

user.setProfile({ email, phone, homepage, languages, prefer });
user.setProfile({ email, phone, homepage, language, prefer });
await this.updateInterests(userId, interestIds);

await this.userRepository.save(user);
Expand Down
16 changes: 0 additions & 16 deletions src/app/application/user/query/IUserQuery.ts

This file was deleted.

25 changes: 19 additions & 6 deletions src/app/application/user/query/listUser/ListUserQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ import { IQuery } from '@nestjs/cqrs';

import { UserState } from '@khlug/app/domain/user/model/constant';

import { Typeof } from '@khlug/util/types';

export class ListUserQuery implements IQuery {
constructor(
readonly state: UserState | null,
readonly interestId: string | null,
readonly limit: number,
readonly offset: number,
) {}
readonly email: string | null;
readonly name: string | null;
readonly college: string | null;
readonly grade: number | null;
readonly state: UserState | null;
readonly limit: number;
readonly offset: number;

constructor(params: Typeof<ListUserQuery>) {
this.email = params.email;
this.name = params.name;
this.college = params.college;
this.grade = params.grade;
this.state = params.state;
this.limit = params.limit;
this.offset = params.offset;
}
}

This file was deleted.

51 changes: 37 additions & 14 deletions src/app/application/user/query/listUser/ListUserQueryHandler.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,56 @@
import { Inject, Injectable } from '@nestjs/common';
import { EntityRepository } from '@mikro-orm/mysql';
import { InjectRepository } from '@mikro-orm/nestjs';
import { Injectable } from '@nestjs/common';
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';

import {
IUserQuery,
UserQuery,
} from '@khlug/app/application/user/query/IUserQuery';
import { ListUserQuery } from '@khlug/app/application/user/query/listUser/ListUserQuery';
import { ListUserQueryResult } from '@khlug/app/application/user/query/listUser/ListUserQueryResult';
import { UserListView } from '@khlug/app/application/user/query/view/UserListView';

import { User } from '@khlug/app/domain/user/model/User';

@Injectable()
@QueryHandler(ListUserQuery)
export class ListUserQueryHandler
implements IQueryHandler<ListUserQuery, ListUserQueryResult>
{
constructor(
@Inject(UserQuery)
private readonly userQuery: IUserQuery,
@InjectRepository(User)
private readonly userRepository: EntityRepository<User>,
) {}

async execute(query: ListUserQuery): Promise<ListUserQueryResult> {
const { state, interestId, limit, offset } = query;
const { email, name, college, grade, state, limit, offset } = query;

const qb = this.userRepository.createQueryBuilder('user');

if (email) {
qb.andWhere('profile.email LIKE ?', [`%${email}%`]);
}

if (name) {
qb.andWhere('profile.name LIKE ?', [`%${name}%`]);
}

if (college) {
qb.andWhere('profile.college LIKE ?', [`%${college}%`]);
}

if (grade) {
qb.andWhere('profile.grade = ?', [grade]);
}

if (state) {
qb.andWhere('profile.state = ?', [state]);
}

const listView = await this.userQuery.listUser({
state,
interestId,
limit,
offset,
});
const [users, count] = await qb
.limit(limit)
.offset(offset)
.orderBy({ id: 'ASC' })
.getResultAndCount();

const listView: UserListView = { count, users };
return new ListUserQueryResult(listView);
}
}
2 changes: 1 addition & 1 deletion src/app/application/user/query/view/UserView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface ProfileView {
email: string | null;
phone: string | null;
homepage: string | null;
languages: string[] | null;
language: string | null;
prefer: string | null;
}

Expand Down
47 changes: 41 additions & 6 deletions src/app/domain/user/model/Profile.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ValueObject } from '@khlug/core/ddd/ValueObject';
import { Embeddable, Property } from '@mikro-orm/core';
import { IsEmail, IsInt, IsOptional, IsString, Length } from 'class-validator';

export type ProfileConstructorParams = {
name: string;
Expand All @@ -8,31 +9,65 @@ export type ProfileConstructorParams = {
email: string | null;
phone: string | null;
homepage: string | null;
languages: string[] | null;
language: string | null;
prefer: string | null;
};

export class Profile extends ValueObject {
@Embeddable()
export class Profile {
@Property({ type: 'varchar', length: 255, name: 'realname' })
@IsString()
@Length(1, 255)
readonly name: string;

@Property({ type: 'varchar', length: 255, name: 'college' })
@IsString()
@Length(1, 255)
readonly college: string;

@Property({ type: 'bigint', length: 20, name: 'grade' })
@IsInt()
readonly grade: number;

@Property({ type: 'bigint', length: 20, name: 'number', nullable: true })
@IsInt()
@IsOptional()
readonly number: number | null;

@Property({ type: 'varchar', length: 255, name: 'email', nullable: true })
@IsEmail()
@IsOptional()
readonly email: string | null;

@Property({ type: 'varchar', length: 255, name: 'phone', nullable: true })
@IsString()
@IsOptional()
readonly phone: string | null;

@Property({ type: 'varchar', length: 255, name: 'homepage', nullable: true })
@IsString()
@IsOptional()
readonly homepage: string | null;
readonly languages: string[] | null;

@Property({ type: 'varchar', length: 255, name: 'language', nullable: true })
@IsString({ each: true })
@IsOptional()
readonly language: string | null;

@Property({ type: 'varchar', length: 255, name: 'prefer', nullable: true })
@IsString()
@IsOptional()
readonly prefer: string | null;

constructor(params: ProfileConstructorParams) {
super();
this.name = params.name;
this.college = params.college;
this.grade = params.grade;
this.number = params.number;
this.email = params.email;
this.phone = params.phone;
this.homepage = params.homepage;
this.languages = params.languages;
this.language = params.language;
this.prefer = params.prefer;
}
}
Loading

0 comments on commit d0d0f1c

Please sign in to comment.