feat: 작업 관리 시스템 및 TBM 공정/작업 통합
## Backend Changes - Create tasks table with work_type_id FK to work_types - Add taskModel, taskController, taskRoutes for task CRUD - Update tbmModel to support work_type_id and task_id - Add migrations for tasks table and TBM integration ## Frontend Changes - Create task management admin page (tasks.html, task-management.js) - Update TBM modal to include work type (공정) and task (작업) selection - Add cascading dropdown: work type → task selection - Display work type and task info in TBM session cards - Update sidebar navigation in all admin pages ## Database Schema - tasks: task_id, work_type_id, task_name, description, is_active - tbm_sessions: add work_type_id, task_id columns with FKs - Foreign keys maintain referential integrity with work_types and tasks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
179
api.hyungi.net/controllers/taskController.js
Normal file
179
api.hyungi.net/controllers/taskController.js
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* 작업 관리 컨트롤러
|
||||
*
|
||||
* 작업 CRUD API 엔드포인트 핸들러
|
||||
* (공정=work_types에 속하는 세부 작업)
|
||||
*
|
||||
* @author TK-FB-Project
|
||||
* @since 2026-01-26
|
||||
*/
|
||||
|
||||
const taskModel = require('../models/taskModel');
|
||||
const { ValidationError, NotFoundError, DatabaseError } = require('../utils/errors');
|
||||
const { asyncHandler } = require('../middlewares/errorHandler');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
// ==================== 작업 CRUD ====================
|
||||
|
||||
/**
|
||||
* 작업 생성
|
||||
*/
|
||||
exports.createTask = asyncHandler(async (req, res) => {
|
||||
const taskData = req.body;
|
||||
|
||||
if (!taskData.task_name) {
|
||||
throw new ValidationError('작업명은 필수 입력 항목입니다');
|
||||
}
|
||||
|
||||
logger.info('작업 생성 요청', { name: taskData.task_name });
|
||||
|
||||
const id = await new Promise((resolve, reject) => {
|
||||
taskModel.createTask(taskData, (err, lastID) => {
|
||||
if (err) reject(new DatabaseError('작업 생성 중 오류가 발생했습니다'));
|
||||
else resolve(lastID);
|
||||
});
|
||||
});
|
||||
|
||||
logger.info('작업 생성 성공', { task_id: id });
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: { task_id: id },
|
||||
message: '작업이 성공적으로 생성되었습니다'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 전체 작업 조회
|
||||
*/
|
||||
exports.getAllTasks = asyncHandler(async (req, res) => {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
taskModel.getAllTasks((err, data) => {
|
||||
if (err) reject(new DatabaseError('작업 목록 조회 중 오류가 발생했습니다'));
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: rows,
|
||||
message: '작업 목록 조회 성공'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 활성 작업만 조회
|
||||
*/
|
||||
exports.getActiveTasks = asyncHandler(async (req, res) => {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
taskModel.getActiveTasks((err, data) => {
|
||||
if (err) reject(new DatabaseError('활성 작업 목록 조회 중 오류가 발생했습니다'));
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: rows,
|
||||
message: '활성 작업 목록 조회 성공'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 공정별 작업 조회
|
||||
*/
|
||||
exports.getTasksByWorkType = asyncHandler(async (req, res) => {
|
||||
const workTypeId = req.params.work_type_id || req.query.work_type_id;
|
||||
|
||||
if (!workTypeId) {
|
||||
throw new ValidationError('공정 ID가 필요합니다');
|
||||
}
|
||||
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
taskModel.getTasksByWorkType(workTypeId, (err, data) => {
|
||||
if (err) reject(new DatabaseError('공정별 작업 목록 조회 중 오류가 발생했습니다'));
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: rows,
|
||||
message: '공정별 작업 목록 조회 성공'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 단일 작업 조회
|
||||
*/
|
||||
exports.getTaskById = asyncHandler(async (req, res) => {
|
||||
const taskId = req.params.id;
|
||||
|
||||
const task = await new Promise((resolve, reject) => {
|
||||
taskModel.getTaskById(taskId, (err, data) => {
|
||||
if (err) reject(new DatabaseError('작업 조회 중 오류가 발생했습니다'));
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
|
||||
if (!task) {
|
||||
throw new NotFoundError('작업을 찾을 수 없습니다');
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: task,
|
||||
message: '작업 조회 성공'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 작업 수정
|
||||
*/
|
||||
exports.updateTask = asyncHandler(async (req, res) => {
|
||||
const taskId = req.params.id;
|
||||
const taskData = req.body;
|
||||
|
||||
if (!taskData.task_name) {
|
||||
throw new ValidationError('작업명은 필수 입력 항목입니다');
|
||||
}
|
||||
|
||||
logger.info('작업 수정 요청', { task_id: taskId });
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
taskModel.updateTask(taskId, taskData, (err, result) => {
|
||||
if (err) reject(new DatabaseError('작업 수정 중 오류가 발생했습니다'));
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
logger.info('작업 수정 성공', { task_id: taskId });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '작업이 성공적으로 수정되었습니다'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 작업 삭제
|
||||
*/
|
||||
exports.deleteTask = asyncHandler(async (req, res) => {
|
||||
const taskId = req.params.id;
|
||||
|
||||
logger.info('작업 삭제 요청', { task_id: taskId });
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
taskModel.deleteTask(taskId, (err, result) => {
|
||||
if (err) reject(new DatabaseError('작업 삭제 중 오류가 발생했습니다'));
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
|
||||
logger.info('작업 삭제 성공', { task_id: taskId });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '작업이 성공적으로 삭제되었습니다'
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user