Skip to content

Commit

Permalink
feat: 태스크 담당자 설정 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
jjeonghak committed Nov 24, 2024
1 parent 6010453 commit 110fae3
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 31 deletions.
14 changes: 13 additions & 1 deletion apps/server/config/typeorm.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SubTask } from '@/task/domain/subTask.entity';
import { Sprint } from '@/project/entity/sprint.entity';
import { Label } from '@/project/entity/label.entity';
import { TaskLabel } from '@/task/domain/task-label.entity';
import { TaskAssignee } from '@/task/domain/task-assignee.entity';

@Injectable()
export class TypeormConfig implements TypeOrmOptionsFactory {
Expand All @@ -23,7 +24,18 @@ export class TypeormConfig implements TypeOrmOptionsFactory {
username: this.configService.get<string>('DATABASE_USER'),
password: this.configService.get<string>('DATABASE_PASSWORD'),
database: this.configService.get<string>('DATABASE_NAME'),
entities: [Task, Section, Account, Project, Contributor, SubTask, Sprint, Label, TaskLabel],
entities: [
Task,
Section,
Account,
Project,
Contributor,
SubTask,
Sprint,
Label,
TaskLabel,
TaskAssignee,
],
synchronize: true,
ssl: {
rejectUnauthorized: false,
Expand Down
4 changes: 1 addition & 3 deletions apps/server/src/project/controller/label.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Delete, HttpCode, Param, Patch, UseGuards } from '@nestjs/common';
import { Body, Controller, Delete, Param, Patch, UseGuards } from '@nestjs/common';
import { AccessTokenGuard } from '@/account/guard/accessToken.guard';
import { LabelService } from '@/project/service/label.service';
import { AuthUser } from '@/account/decorator/authUser.decorator';
Expand All @@ -12,7 +12,6 @@ export class LabelController {
constructor(private labelService: LabelService) {}

@Patch(':id')
@HttpCode(200)
async update(
@AuthUser() user: Account,
@Param('id') id: number,
Expand All @@ -26,7 +25,6 @@ export class LabelController {
}

@Delete(':id')
@HttpCode(200)
async delete(@AuthUser() user: Account, @Param('id') id: number) {
await this.labelService.delete(user.id, id);
return new BaseResponse(200, '라벨 삭제 완료했습니다.', {});
Expand Down
6 changes: 0 additions & 6 deletions apps/server/src/project/controller/project.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export class ProjectController {
) {}

@Get('invitations')
@HttpCode(200)
async getInvitations(@AuthUser() user: Account) {
return new BaseResponse(
200,
Expand All @@ -46,7 +45,6 @@ export class ProjectController {
}

@Get(':id')
@HttpCode(200)
async getProject(@AuthUser() user: Account, @Param('id') projectId: number) {
return new BaseResponse(
200,
Expand All @@ -56,7 +54,6 @@ export class ProjectController {
}

@Get(':id/members')
@HttpCode(200)
async getMembers(@AuthUser() user: Account, @Param('id') projectId: number) {
return new BaseResponse(
200,
Expand Down Expand Up @@ -88,7 +85,6 @@ export class ProjectController {
}

@Patch(':id/invite')
@HttpCode(200)
async updateInvitation(
@AuthUser() user: Account,
@Param('id') projectId: number,
Expand Down Expand Up @@ -144,7 +140,6 @@ export class ProjectController {
}

@Get(':id/sprints')
@HttpCode(200)
async getSprints(@AuthUser() user: Account, @Param('id') id: number) {
return new BaseResponse(
200,
Expand All @@ -167,7 +162,6 @@ export class ProjectController {
}

@Get(':id/labels')
@HttpCode(200)
async getLabels(@AuthUser() user: Account, @Param('id') id: number) {
return new BaseResponse(
200,
Expand Down
4 changes: 1 addition & 3 deletions apps/server/src/project/controller/sprint.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Delete, HttpCode, Param, Patch, UseGuards } from '@nestjs/common';
import { Body, Controller, Delete, Param, Patch, UseGuards } from '@nestjs/common';
import { AccessTokenGuard } from '@/account/guard/accessToken.guard';
import { SprintService } from '@/project/service/sprint.service';
import { AuthUser } from '@/account/decorator/authUser.decorator';
Expand All @@ -12,7 +12,6 @@ export class SprintController {
constructor(private sprintService: SprintService) {}

@Patch(':id')
@HttpCode(200)
async update(
@AuthUser() user: Account,
@Param('id') id: number,
Expand All @@ -26,7 +25,6 @@ export class SprintController {
}

@Delete(':id')
@HttpCode(200)
async delete(@AuthUser() user: Account, @Param('id') id: number) {
await this.sprintService.delete(user.id, id);
return new BaseResponse(200, '스프린트 삭제 완료했습니다.', {});
Expand Down
4 changes: 1 addition & 3 deletions apps/server/src/task/controller/subTask.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Delete, HttpCode, Param, Patch, UseGuards } from '@nestjs/common';
import { Body, Controller, Delete, Param, Patch, UseGuards } from '@nestjs/common';
import { AuthUser } from '@/account/decorator/authUser.decorator';
import { Account } from '@/account/entity/account.entity';
import { AccessTokenGuard } from '@/account/guard/accessToken.guard';
Expand All @@ -12,7 +12,6 @@ export class SubTaskController {
constructor(private subTaskService: SubTaskService) {}

@Patch(':id')
@HttpCode(200)
async update(
@AuthUser() user: Account,
@Param('id') subTaskId: number,
Expand All @@ -26,7 +25,6 @@ export class SubTaskController {
}

@Delete(':id')
@HttpCode(200)
async delete(@AuthUser() user: Account, @Param('id') subTaskId: number) {
await this.subTaskService.delete(user.id, subTaskId);
return new BaseResponse(200, '서브 태스크 삭제 완료했습니다.', {});
Expand Down
30 changes: 15 additions & 15 deletions apps/server/src/task/controller/task.controller.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
import {
Body,
Controller,
Get,
HttpCode,
Param,
Patch,
Post,
Put,
Query,
UseGuards,
} from '@nestjs/common';
import { Body, Controller, Get, Param, Patch, Post, Put, Query, UseGuards } from '@nestjs/common';
import { TaskService } from '@/task/service/task.service';
import { AuthUser } from '@/account/decorator/authUser.decorator';
import { Account } from '@/account/entity/account.entity';
Expand All @@ -19,6 +8,7 @@ import { UpdateTaskDetailsRequest } from '@/task/dto/update-task-details-request
import { SubTaskService } from '@/task/service/subTask.service';
import { CreateSubTaskRequest } from '@/task/dto/create-subTask-request.dto';
import { UpdateLabelsRequest } from '@/task/dto/update-labels-request.dto';
import { UpdateAssigneesRequest } from '@/task/dto/update-assignees-request.dto';

@UseGuards(AccessTokenGuard)
@Controller('task')
Expand All @@ -29,7 +19,6 @@ export class TaskController {
) {}

@Get()
@HttpCode(200)
async getAll(@AuthUser() user: Account, @Query('projectId') projectId: number) {
return new BaseResponse(
200,
Expand All @@ -39,7 +28,6 @@ export class TaskController {
}

@Get(':id')
@HttpCode(200)
async get(@AuthUser() user: Account, @Param('id') id: number) {
return new BaseResponse(
200,
Expand All @@ -49,7 +37,6 @@ export class TaskController {
}

@Patch(':id')
@HttpCode(200)
async updateDetails(
@AuthUser() user: Account,
@Param('id') id: number,
Expand Down Expand Up @@ -87,4 +74,17 @@ export class TaskController {
await this.taskService.updateLabels(user.id, id, body.labels)
);
}

@Put(':id/assignees')
async updateAssignees(
@AuthUser() user: Account,
@Param('id') id: number,
@Body() body: UpdateAssigneesRequest
) {
return new BaseResponse(
200,
'태스크 담당자 수정 완료했습니다.',
await this.taskService.updateAssignees(user.id, id, body.assignees)
);
}
}
14 changes: 14 additions & 0 deletions apps/server/src/task/domain/task-assignee.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { EntityTimestamp } from '@/common/entity-timestamp.entity';

@Entity()
export class TaskAssignee extends EntityTimestamp {
@PrimaryGeneratedColumn()
id: number;

@Column()
taskId: number;

@Column()
accountId: number;
}
13 changes: 13 additions & 0 deletions apps/server/src/task/dto/assignee-details-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export class AssigneeDetailsResponse {
constructor(id: number, username: string, avatar: string) {
this.id = id;
this.username = username;
this.avatar = avatar;
}

id: number;

username: string;

avatar: string;
}
8 changes: 8 additions & 0 deletions apps/server/src/task/dto/update-assignees-request.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IsArray, IsDefined, IsInt } from 'class-validator';

export class UpdateAssigneesRequest {
@IsDefined()
@IsArray()
@IsInt({ each: true })
assignees: number[];
}
43 changes: 43 additions & 0 deletions apps/server/src/task/service/task.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { Sprint } from '@/project/entity/sprint.entity';
import { Label } from '@/project/entity/label.entity';
import { TaskLabel } from '@/task/domain/task-label.entity';
import { LabelDetailsResponse } from '@/project/dto/label-details-response.dto';
import { TaskAssignee } from '@/task/domain/task-assignee.entity';
import { AssigneeDetailsResponse } from '../dto/assignee-details-response.dto';

const { defaultType: json0 } = ShareDB.types;

Expand Down Expand Up @@ -246,6 +248,47 @@ export class TaskService {
return { taskId, labels: labels.map((label) => new LabelDetailsResponse(label)) };
}

async updateAssignees(userId: number, taskId: number, assigneeIds: number[]) {
const task = await this.findTaskOrThrow(taskId);
await this.validateUserRole(userId, task.section.project.id);
let records = [];
if (assigneeIds.length !== 0) {
records = await this.contributorRepository
.createQueryBuilder('c')
.leftJoin('account', 'a', 'c.userId = a.id')
.where('c.projectId = :projectId', { projectId: task.section.project.id })
.andWhere('c.status = :status', { status: ContributorStatus.ACCEPTED })
.andWhere('c.userId IN (:...userIds)', { userIds: assigneeIds })
.addSelect(['a.username'])
.getRawMany();
}
if (records.length !== assigneeIds.length) {
throw new NotFoundException('Assignees not found');
}

const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
await queryRunner.manager.delete(TaskAssignee, { taskId });
for (let i = 0; i < assigneeIds.length; i += 1) {
await queryRunner.manager.save(TaskAssignee, { taskId, accountId: assigneeIds[i] });
}
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
return {
taskId,
assignees: records.map(
(record) => new AssigneeDetailsResponse(record.c_userId, record.a_username, '')
),
};
}

private async validateUserRole(userId: number, projectId: number) {
const contributor = await this.contributorRepository.findOneBy({ projectId, userId });
if (!contributor || contributor.status !== ContributorStatus.ACCEPTED) {
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/task/task.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { SubTaskController } from '@/task/controller/subTask.controller';
import { Sprint } from '@/project/entity/sprint.entity';
import { TaskLabel } from '@/task/domain/task-label.entity';
import { Label } from '@/project/entity/label.entity';
import { TaskAssignee } from '@/task/domain/task-assignee.entity';

@Module({
imports: [
Expand All @@ -26,6 +27,7 @@ import { Label } from '@/project/entity/label.entity';
Sprint,
Label,
TaskLabel,
TaskAssignee,
]),
],
controllers: [TaskController, EventController, SubTaskController],
Expand Down

0 comments on commit 110fae3

Please sign in to comment.