꽤나 잘 진행됐던 이번 프로젝트, 뷰페이지 구현하지 않고 서버로만 보여줬기로 했던터라 백엔드 구현에 더욱 집중했다.
트렐로를 보면 각 카드별로 작업자를 할당할 수 있는데 그 로직을 우리 프로젝트에도 추가해보았다.
import {
Column,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Card } from './card.entity';
import { IsNumber } from 'class-validator';
import { User } from 'src/user/entities/user.entity';
import { ApiProperty } from '@nestjs/swagger';
@Entity('card_workers')
export class CardWorker {
@PrimaryGeneratedColumn({ unsigned: true })
id: number;
@IsNumber()
@Column()
@ApiProperty({ description: '유저 아이디', example: '1' })
user_id: number;
// 유저 테이블과 N:1
@ManyToOne(() => User, (user) => user.cardWorkers, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'user_id' })
user: User;
// 카드 테이블과 N:1
@ManyToOne(() => Card, (card) => card.cardWorkers, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'card_id' })
card: Card;
}
###
#CREATE WORKER
POST {{host}}/api/7/card/80/worker/create
Authorization: Bearer {{Token}}
Content-Type: application/json
{
"userIds": [
{ "id": 4 },
{ "id": 5 }
]
}
###
# DELETE WORKER
DELETE {{host}}/api/7/card/80/worker/remove/4
Authorization: Bearer {{Token}}
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, IsNotEmpty } from 'class-validator';
export class CreateWorkerDto {
@ApiProperty({
description: '작업자 할당입니다.',
example: '[{"id":1},{"id":2}]',
})
@IsNotEmpty()
@IsArray()
userIds: { id: number }[];
}
async getAllWorkers(boardId: number) {
return await this.boardMemberRepository.find({
where: { board: { id: boardId } },
});
}
고려해야할 사항
- 작업자는 초대된 멤버만 할당될 수 있다.
- 중복돼서 할당될 수 없다.
async createWorker(
boardId: number,
cardId: number,
createWorkerDto: CreateWorkerDto,
) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
// 초대된 멤버인지 체크
const getWorkers = await this.getAllWorkers(boardId);
// 초대된 멤버의 아이디 값만 넣어줄 배열 생성
const invitedWorkerArr = [];
// 할당된 작업자들만 넣어줄 배열 생성
const createdWorkers = [];
// 초대된 멤버 아이디 값 넣어주기
getWorkers.forEach((worker) => {
invitedWorkerArr.push(worker.userId);
});
const { userIds } = createWorkerDto;
for (const user of userIds) {
// 해당 유저가 멤버인지 확인
if (!invitedWorkerArr.includes(user.id))
throw new NotFoundException('초대되지 않은 멤버입니다.');
// 작업자 중복 체크
const existingWorker = await this.cardWorkerRepository.find({
where: { user_id: user.id },
});
// 중복된 사람 제외 등록
if (existingWorker.length > 0) {
throw new Error('중복된 멤버입니다.');
}
const newWorker = await queryRunner.manager.save(CardWorker, {
user_id: user.id,
card: { id: cardId },
});
createdWorkers.push(newWorker);
}
await queryRunner.commitTransaction();
return createdWorkers;
} catch (error) {
await queryRunner.rollbackTransaction();
return { status: 404, message: error.message };
} finally {
// 사용이 끝난 후에는 항상 queryRunner를 해제
await queryRunner.release();
}
}
async removeWorker(cardId: number, userId: number) {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
// 해당되는 사용자가 있는 지 확인
const existingWorker = await this.cardWorkerRepository.findOne({
where: { user: { id: userId }, card: { id: cardId } },
});
// 없다면?
if (!existingWorker)
throw new NotFoundException('해당되는 사용자가 없습니다.');
// 있으면 삭제하기
const deleteWorker = await this.cardWorkerRepository.delete({
user: { id: userId },
});
await queryRunner.commitTransaction();
return deleteWorker;
} catch (error) {
await queryRunner.rollbackTransaction();
return { status: 404, message: error.message };
} finally {
// 사용이 끝난 후에는 항상 queryRunner를 해제
await queryRunner.release();
}
}