Skip to content
Merged

merge #314

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/controllers/task.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,10 @@ class TaskController {
}
}

// ํŒ€์› ์ •๋ณด ์ˆ˜์ • (์—ญํ•  ๋ณ€๊ฒฝ)
async updateTeamMember(req, res, next) {
try {
const taskId = req.body.taskId || req.params.taskId;
const userId = req.body.userId || req.params.userId;
const { role } = req.body;
const { taskId, userId } = req.params;
const { role } = req.body; // ํ”„๋ก ํŠธ์—์„œ 0(Owner) ๋˜๋Š” 1(Member)์ด ์˜ด

const result = await taskService.modifyMemberRole(
parseInt(taskId),
Expand All @@ -201,12 +199,13 @@ class TaskController {

res.status(200).json({
resultType: "SUCCESS",
message: "์š”์ฒญ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.",
message: "๋ฉค๋ฒ„ ๊ถŒํ•œ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.",
data: {
memberId: result.id,
userId: result.userId,
taskId: result.taskId,
role: result.role ? 1 : 0,
// ๐Ÿ“ DB๊ฐ€ false(0)๋ฉด 0(Owner), true(1)๋ฉด 1(Member) ๋ฐ˜ํ™˜
role: result.role ? 1 : 0
}
});
} catch (error) {
Expand Down
30 changes: 15 additions & 15 deletions src/repositories/task.repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,34 +177,34 @@ class TaskRepository {
});
}

// ๋ฉค๋ฒ„ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
async findMemberInTask(taskId, userId) {
return await prisma.member.findFirst({
where: {
taskId: parseInt(taskId),
userId: parseInt(userId)
}
});
}

// ๋‚˜๋จธ์ง€ ๋ฉค๋ฒ„ ์—ญํ•  ๋ฆฌ์…‹
// ๋‚˜๋จธ์ง€ ๋ฉค๋ฒ„ ์—ญํ•  ๋ฆฌ์…‹ (๋ฐฉ์žฅ ํ•œ ๋ช…์„ ์ œ์™ธํ•˜๊ณ  ๋ชจ๋‘ ๋ฉค๋ฒ„๋กœ)
async resetOtherMembersRole(taskId, userId, tx) {
return await tx.member.updateMany({
where: {
taskId: parseInt(taskId),
userId: { not: parseInt(userId) },
},
data: {
role: false,
role: true,
},
});
}

// ๋Œ€์ƒ ๋ฉค๋ฒ„ ์—ญํ•  ์—…๋ฐ์ดํŠธ
async updateMemberRole(memberId, isAdmin, tx) {
async updateMemberRole(memberId, dbRoleValue, tx) {
return await tx.member.update({
where: { id: memberId }, // userId๋Š” ์ค‘๋ณต๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ(๋‹ค๋ฅธ ๊ณผ์ œ ๋“ฑ) id(PK) ์‚ฌ์šฉ
data: { role: isAdmin },
where: { id: memberId },
data: { role: dbRoleValue },
});
}

// ๋ฉค๋ฒ„ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
async findMemberInTask(taskId, userId) {
return await prisma.member.findFirst({
where: {
taskId: parseInt(taskId),
userId: parseInt(userId)
}
});
}

Expand Down
157 changes: 73 additions & 84 deletions src/services/task.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,46 @@ class TaskService {
return await taskRepository.getCompletedTasks(userId);
}

// ๊ณผ์ œ ๋“ฑ๋ก
async getTaskList(userId, queryParams = {}) {
let { type, folderId, sort, status } = queryParams;

const myTeamFolder = await prisma.folder.findFirst({
where: { userId, folderTitle: "ํŒ€" }
});

if (folderId && myTeamFolder && parseInt(folderId) === myTeamFolder.id) {
folderId = undefined;
type = 'TEAM';
}

// 3. ์‹ค์ œ DB ์กฐํšŒ
const tasks = await taskRepository.findAllTasks({
userId,
type,
folderId,
sort,
status
});

return tasks.map(task => {
if (task.type === 'TEAM' && myTeamFolder) {
return {
...task,
folderId: myTeamFolder.id,
folderTitle: myTeamFolder.folderTitle,
foldercolor: myTeamFolder.color
};
}
return task;
});
}

async registerTask(userId, data) {
const { subTasks, references, folderId, ...taskData } = data;

console.log("์ƒ์„ฑ ์‹œ๋„ ์œ ์ € ID:", userId)
console.log("์ƒ์„ฑ ์‹œ๋„ ์œ ์ € ID:", userId);

// 1. ์œ ์ € ์กด์žฌ ํ™•์ธ
const user = await prisma.user.findUnique({
where: { id: userId }
});
Expand All @@ -32,57 +67,42 @@ class TaskService {

if (!taskData.title) throw new BadRequestError("๊ณผ์ œ๋ช…์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.");

// folderId๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ํด๋” ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
if (folderId) {
const folder = await taskRepository.findFolderById(folderId);
if (!folder) throw new NotFoundError("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํด๋”์ž…๋‹ˆ๋‹ค.");
}

let targetFolderId = folderId;

if (taskData.type === 'TEAM') {
// 1. ํŒ€ ๊ณผ์ œ๋ผ๋ฉด ๋ฌด์กฐ๊ฑด 'ํŒ€' ํด๋”๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
const teamFolder = await prisma.folder.findFirst({
where: {
userId: userId,
folderTitle: "ํŒ€"
}
});

if (!teamFolder) {
throw new NotFoundError("ํŒ€ ๊ณผ์ œ ์ „์šฉ ํด๋”๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
}
let folder = null;

// 2. ๊ฐ•์ œ๋กœ 'ํŒ€' ํด๋” ID๋กœ
targetFolderId = teamFolder.id;

} else if (folderId) {
// 3. ๊ฐœ์ธ ๊ณผ์ œ์ธ๋ฐ ํด๋”๋ฅผ ์„ ํƒํ•œ ๊ฒฝ์šฐ -> ์œ ํšจ์„ฑ ๋ฐ ๊ถŒํ•œ ๊ฒ€์‚ฌ
const folder = await taskRepository.findFolderById(folderId);
if (folderId) {
folder = await taskRepository.findFolderById(folderId);

// ํด๋”๊ฐ€ ์—†์œผ๋ฉด ์—๋Ÿฌ
if (!folder) {
throw new NotFoundError("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํด๋”์ž…๋‹ˆ๋‹ค.");
}

// ๋‚ด ํด๋”๊ฐ€ ์•„๋‹ˆ๋ฉด ์—๋Ÿฌ (๋ณด์•ˆ)
if (folder.userId !== userId) {
throw new ForbiddenError("๊ถŒํ•œ์ด ์—†๋Š” ํด๋”์ž…๋‹ˆ๋‹ค.");
}
}
// CASE A: ํŒ€ ๊ณผ์ œ ('TEAM')


// CASE A: ํŒ€ ๊ณผ์ œ ('TEAM')
if (taskData.type === 'TEAM') {
// ๊ทœ์น™: "ํŒ€" ํด๋”๊ฐ€ ์•„๋‹ˆ๋ฉด ๋ฌด์กฐ๊ฑด ์—๋Ÿฌ! (์„ ํƒ ์•ˆ ํ•ด๋„ ์—๋Ÿฌ, ๋‹ค๋ฅธ ํด๋”์—ฌ๋„ ์—๋Ÿฌ)
// โš ๏ธ ์ฃผ์˜: DB ํด๋”๋ช…์ด "ํŒ€ ๊ณผ์ œ"๋ผ๋ฉด ์—ฌ๊ธฐ๋„ "ํŒ€ ๊ณผ์ œ"๋กœ ์ˆ˜์ •ํ•˜์„ธ์š”.
if (!folder || folder.folderTitle !== "ํŒ€") {
throw new BadRequestError("INVALID_FOLDER", "ํŒ€ ๊ณผ์ œ๋Š” 'ํŒ€' ํด๋”์—๋งŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.");
}
}
// CASE B: ๊ฐœ์ธ ๊ณผ์ œ ('PERSONAL')
else {
// ๊ทœ์น™: "ํŒ€" ํด๋”๋ฅผ ์„ ํƒํ–ˆ๋‹ค๋ฉด ์—๋Ÿฌ! (ํŒ€ ํด๋” ์นจ๋ฒ” ๋ถˆ๊ฐ€)
if (folder && folder.folderTitle === "ํŒ€") {
throw new BadRequestError("INVALID_FOLDER", "๊ฐœ์ธ ๊ณผ์ œ๋Š” 'ํŒ€' ํด๋”์— ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
}
}

// ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
return await prisma.$transaction(async (tx) => {
// ๊ณผ์ œ ์ƒ์„ฑ
// ๊ณผ์ œ ์ƒ์„ฑ (๊ฒ€์ฆ๋œ folderId ์‚ฌ์šฉ)
const newTask = await taskRepository.createTask({ ...taskData, folderId }, tx);

// ๊ณผ์ œ ์ƒ์„ฑ์ž๋ฅผ owner๋กœ ๋ฉค๋ฒ„์— ์ž๋™ ์ถ”๊ฐ€
Expand All @@ -92,18 +112,15 @@ class TaskService {
const maxRank = await taskRepository.findMaxRank(userId, tx);
await taskRepository.upsertTaskPriority(userId, newTask.id, maxRank + 1, tx);

// ๊ณผ์ œ ์•Œ๋ฆผ ์ƒ์„ฑ
// --- [์•Œ๋ฆผ ์ƒ์„ฑ ๋กœ์ง ์œ ์ง€] ---
if (newTask.isAlarm) {
// ํŒ€ ๊ณผ์ œ์ธ ๊ฒฝ์šฐ: ๋ฉค๋ฒ„ ๋ชจ๋‘์—๊ฒŒ ์•Œ๋ฆผ ์ƒ์„ฑ
if (newTask.type === 'TEAM') {
// ๋ฉค๋ฒ„ ์กฐํšŒ (์ƒ์„ฑ์ž ํฌํ•จ)
const members = await tx.member.findMany({
where: { taskId: newTask.id },
include: { user: true },
});

if (members.length > 0) {
// ๋ชจ๋“  ๋ฉค๋ฒ„์—๊ฒŒ ์•Œ๋ฆผ ์ƒ์„ฑ
const alarmPromises = members.map(async (member) => {
const user = member.user;
const alarmHours = user.deadlineAlarm || 24;
Expand All @@ -120,7 +137,6 @@ class TaskService {
await Promise.all(alarmPromises);
}
} else {
// ๊ฐœ์ธ ๊ณผ์ œ์ธ ๊ฒฝ์šฐ: ์ƒ์„ฑ์ž์—๊ฒŒ๋งŒ ์•Œ๋ฆผ ์ƒ์„ฑ
const creator = await tx.user.findUnique({
where: { id: userId },
select: { deadlineAlarm: true },
Expand All @@ -141,18 +157,16 @@ class TaskService {
}
}

// ํ•˜์œ„ ๋ฐ์ดํ„ฐ ์ €์žฅ
// --- [ํ•˜์œ„ ๋ฐ์ดํ„ฐ ์ €์žฅ ๋กœ์ง ์œ ์ง€] ---
if (subTasks && subTasks.length > 0) {
await taskRepository.addSubTasks(newTask.id, subTasks, tx);

// ์„ธ๋ถ€๊ณผ์ œ ์ƒ์„ฑ ํ›„ ์•Œ๋ฆผ ์ƒ์„ฑ
const createdSubTasksList = await tx.subTask.findMany({
where: { taskId: newTask.id },
include: { assignee: true },
});

for (const subTask of createdSubTasksList) {
// ์„ธ๋ถ€๊ณผ์ œ ๋‹ด๋‹น์ž์—๊ฒŒ ์•Œ๋ฆผ ์ƒ์„ฑ
if (subTask.isAlarm && subTask.assigneeId) {
const assignee = subTask.assignee;
if (assignee) {
Expand Down Expand Up @@ -193,10 +207,18 @@ class TaskService {
taskData.deadline = new Date(taskData.deadline);
}

// ํด๋”
// ํด๋” ๋ณ€๊ฒฝ ์‹œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ (์ถ”๊ฐ€๋œ ๋ถ€๋ถ„)
if (folderId) {
const folder = await taskRepository.findFolderById(folderId);
if (!folder) throw new NotFoundError("๋ณ€๊ฒฝํ•˜๋ ค๋Š” ํด๋”๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");

// [๋ณดํ˜ธ ๋กœ์ง] ์ˆ˜์ • ์‹œ์—๋„ ํŒ€/๊ฐœ์ธ ํด๋” ๊ทœ์น™ ์ ์šฉ
if (currentTask.type === 'TEAM' && folder.folderTitle !== 'ํŒ€') {
throw new BadRequestError("INVALID_FOLDER", "ํŒ€ ๊ณผ์ œ๋Š” 'ํŒ€' ํด๋”๋กœ๋งŒ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.");
}
if (currentTask.type === 'PERSONAL' && folder.folderTitle === 'ํŒ€') {
throw new BadRequestError("INVALID_FOLDER", "๊ฐœ์ธ ๊ณผ์ œ๋Š” 'ํŒ€' ํด๋”๋กœ ์ด๋™ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
}
}

// ํŠธ๋žœ์žญ์…˜
Expand Down Expand Up @@ -259,6 +281,7 @@ class TaskService {
return { taskId: updatedTask.id };
});
}

// Task ๋งˆ๊ฐ์ผ ๋ณ€๊ฒฝ ์„œ๋น„์Šค
async updateTaskDeadline(userId, taskId, deadline) {
// 1. Task ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
Expand Down Expand Up @@ -337,40 +360,6 @@ class TaskService {
return task;
}

// ๊ณผ์ œ ๋ชฉ๋ก ์กฐํšŒ
async getTaskList(userId, queryParams = {}) {
const { type, folderId, sort, status } = queryParams;

const myTeamFolder = await prisma.folder.findFirst({
where: { userId, folderTitle: "ํŒ€" }
});

if (folderId && myTeamFolder && parseInt(folderId) === myTeamFolder.id) {
folderId = undefined;
type = 'TEAM';
}

const tasks = await taskRepository.findAllTasks({
userId,
type,
folderId,
sort,
status
});

return tasks.map(task => {
if (task.type === 'TEAM' && myTeamFolder) {
return {
...task,
folderId: myTeamFolder.id,
folderTitle: myTeamFolder.folderTitle,
foldercolor: myTeamFolder.color
};
}
return task;
});
}

// ์šฐ์„ ์ˆœ์œ„ ๋ณ€๊ฒฝ
async updatePriorities(userId, orderedTasks) {
// ์ผ๊ด„ ๋ณ€๊ฒฝ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ
Expand Down Expand Up @@ -771,26 +760,26 @@ class TaskService {
});
}

// ํŒ€์› ์ •๋ณด ์ˆ˜์ •
// ๋ฉค๋ฒ„ ์—ญํ•  ์ˆ˜์ •
async modifyMemberRole(taskId, userId, role) {
const member = await taskRepository.findMemberInTask(taskId, userId);

if (!member) throw new NotFoundError("ํ•ด๋‹น ๊ณผ์ œ์—์„œ ํ•ด๋‹น ์œ ์ €๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ");
if (!member) throw new NotFoundError("ํ•ด๋‹น ๊ณผ์ œ์—์„œ ํ•ด๋‹น ์œ ์ €๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ");

const isAdmin = role === 1;
const isTargetBecomingOwner = (role === 0);

return await prisma.$transaction(async (tx) => {
if (isAdmin) {
await taskRepository.resetOtherMembersRole(taskId, userId, tx);
}
return await prisma.$transaction(async (tx) => {
if (isTargetBecomingOwner) {
await taskRepository.resetOtherMembersRole(taskId, userId, tx);
}

return await taskRepository.updateMemberRole(member.id, isAdmin, tx);
});
const dbRoleValue = isTargetBecomingOwner ? false : true;

return await taskRepository.updateMemberRole(member.id, dbRoleValue, tx);
});
}

// ๋‹จ์ผ ์„ธ๋ถ€ ๊ณผ์ œ ์ƒ์„ฑ ์„œ๋น„์Šค
async createSingleSubTask(userId, taskId, data) {
console.log("๐Ÿ“ ์„œ๋น„์Šค๋กœ ๋„˜์–ด์˜จ taskId:", taskId);
const { title, deadline, isAlarm } = data;

// ๋ถ€๋ชจ ๊ณผ์ œ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
Expand Down