feat: 초기 프로젝트 설정 및 룰.md 파일 추가
This commit is contained in:
178
api.hyungi.net/controllers/authController.js
Normal file
178
api.hyungi.net/controllers/authController.js
Normal file
@@ -0,0 +1,178 @@
|
||||
const { getDb } = require('../dbPool');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
exports.login = async (req, res) => {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
const db = await getDb();
|
||||
|
||||
const [rows] = await db.query(
|
||||
'SELECT * FROM Users WHERE username = ?',
|
||||
[username]
|
||||
);
|
||||
|
||||
if (rows.length === 0) {
|
||||
return res.status(401).json({ error: '존재하지 않는 사용자입니다.' });
|
||||
}
|
||||
|
||||
const user = rows[0];
|
||||
const isMatch = await bcrypt.compare(password, user.password);
|
||||
if (!isMatch) {
|
||||
return res.status(401).json({ error: '비밀번호가 일치하지 않습니다.' });
|
||||
}
|
||||
|
||||
// JWT 토큰 생성
|
||||
const token = jwt.sign(
|
||||
{
|
||||
user_id: user.user_id,
|
||||
username: user.username,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
access_level: user.access_level,
|
||||
worker_id: user.worker_id
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1d' }
|
||||
);
|
||||
|
||||
// 토큰 포함 응답
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
token,
|
||||
user_id: user.user_id,
|
||||
username: user.username,
|
||||
role: user.role
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('[로그인 오류]', err);
|
||||
return res.status(500).json({
|
||||
error: '서버 내부 오류',
|
||||
detail: err.message || String(err)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ 사용자 등록 기능 추가
|
||||
exports.register = async (req, res) => {
|
||||
try {
|
||||
const { username, password, name, access_level, worker_id } = req.body;
|
||||
const db = await getDb();
|
||||
|
||||
// 필수 필드 검증
|
||||
if (!username || !password || !name || !access_level) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: '필수 정보가 누락되었습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
// 중복 아이디 확인
|
||||
const [existing] = await db.query(
|
||||
'SELECT user_id FROM Users WHERE username = ?',
|
||||
[username]
|
||||
);
|
||||
|
||||
if (existing.length > 0) {
|
||||
return res.status(409).json({
|
||||
success: false,
|
||||
error: '이미 존재하는 아이디입니다.'
|
||||
});
|
||||
}
|
||||
|
||||
// 비밀번호 해시화
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
// role 설정 (access_level에 따라)
|
||||
const roleMap = {
|
||||
'admin': 'admin',
|
||||
'system': 'admin',
|
||||
'group_leader': 'leader',
|
||||
'support_team': 'support',
|
||||
'worker': 'user'
|
||||
};
|
||||
const role = roleMap[access_level] || 'user';
|
||||
|
||||
// 사용자 등록
|
||||
const [result] = await db.query(
|
||||
`INSERT INTO Users (username, password, name, role, access_level, worker_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
[username, hashedPassword, name, role, access_level, worker_id]
|
||||
);
|
||||
|
||||
console.log('[사용자 등록 성공]', username);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
message: '사용자 등록이 완료되었습니다.',
|
||||
user_id: result.insertId
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('[사용자 등록 오류]', err);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: '서버 오류가 발생했습니다.',
|
||||
detail: err.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ 사용자 삭제 기능 추가
|
||||
exports.deleteUser = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const db = await getDb();
|
||||
|
||||
// 사용자 존재 확인
|
||||
const [user] = await db.query(
|
||||
'SELECT user_id FROM Users WHERE user_id = ?',
|
||||
[id]
|
||||
);
|
||||
|
||||
if (user.length === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: '해당 사용자를 찾을 수 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
// 사용자 삭제
|
||||
await db.query('DELETE FROM Users WHERE user_id = ?', [id]);
|
||||
|
||||
console.log('[사용자 삭제 성공] ID:', id);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: '사용자가 삭제되었습니다.'
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error('[사용자 삭제 오류]', err);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: '서버 오류가 발생했습니다.',
|
||||
detail: err.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 모든 사용자 목록 조회
|
||||
exports.getAllUsers = async (req, res) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
|
||||
// 비밀번호 제외하고 조회
|
||||
const [rows] = await db.query(
|
||||
`SELECT user_id, username, name, role, access_level, worker_id, created_at
|
||||
FROM Users
|
||||
ORDER BY created_at DESC`
|
||||
);
|
||||
|
||||
res.status(200).json(rows);
|
||||
} catch (err) {
|
||||
console.error('[사용자 목록 조회 실패]', err);
|
||||
res.status(500).json({ error: '서버 오류' });
|
||||
}
|
||||
};
|
||||
85
api.hyungi.net/controllers/cuttingPlanController.js
Normal file
85
api.hyungi.net/controllers/cuttingPlanController.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// controllers/cuttingPlanController.js
|
||||
const cuttingPlanModel = require('../models/cuttingPlanModel');
|
||||
|
||||
// 1. 생성
|
||||
exports.createCuttingPlan = async (req, res) => {
|
||||
try {
|
||||
const planData = req.body;
|
||||
const lastID = await new Promise((resolve, reject) => {
|
||||
cuttingPlanModel.create(planData, (err, id) => {
|
||||
if (err) return reject(err);
|
||||
resolve(id);
|
||||
});
|
||||
});
|
||||
res.json({ success: true, cutting_plan_id: lastID });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 전체 조회
|
||||
exports.getAllCuttingPlans = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
cuttingPlanModel.getAll((err, data) => {
|
||||
if (err) return reject(err);
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 단일 조회
|
||||
exports.getCuttingPlanById = async (req, res) => {
|
||||
try {
|
||||
const cutting_plan_id = parseInt(req.params.cutting_plan_id, 10);
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
cuttingPlanModel.getById(cutting_plan_id, (err, data) => {
|
||||
if (err) return reject(err);
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'CuttingPlan not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 수정
|
||||
exports.updateCuttingPlan = async (req, res) => {
|
||||
try {
|
||||
const cutting_plan_id = parseInt(req.params.cutting_plan_id, 10);
|
||||
const planData = { ...req.body, cutting_plan_id };
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
cuttingPlanModel.update(planData, (err, count) => {
|
||||
if (err) return reject(err);
|
||||
resolve(count);
|
||||
});
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'No changes or not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 삭제
|
||||
exports.removeCuttingPlan = async (req, res) => {
|
||||
try {
|
||||
const cutting_plan_id = parseInt(req.params.cutting_plan_id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
cuttingPlanModel.remove(cutting_plan_id, (err, count) => {
|
||||
if (err) return reject(err);
|
||||
resolve(count);
|
||||
});
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'CuttingPlan not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
110
api.hyungi.net/controllers/dailyIssueReportController.js
Normal file
110
api.hyungi.net/controllers/dailyIssueReportController.js
Normal file
@@ -0,0 +1,110 @@
|
||||
const dailyIssueReportModel = require('../models/dailyIssueReportModel');
|
||||
|
||||
// 1. CREATE: 단일 또는 다중 등록 (worker_id 배열 지원)
|
||||
exports.createDailyIssueReport = async (req, res) => {
|
||||
try {
|
||||
const body = req.body;
|
||||
|
||||
// 기본 필드
|
||||
const base = {
|
||||
date: body.date,
|
||||
project_id: body.project_id,
|
||||
start_time: body.start_time,
|
||||
end_time: body.end_time,
|
||||
issue_type_id: body.issue_type_id
|
||||
};
|
||||
|
||||
if (!base.date || !base.project_id || !base.start_time || !base.end_time || !base.issue_type_id || !body.worker_id) {
|
||||
return res.status(400).json({ error: '필수 필드 누락' });
|
||||
}
|
||||
|
||||
// worker_id 배열화
|
||||
const workers = Array.isArray(body.worker_id) ? body.worker_id : [body.worker_id];
|
||||
const insertedIds = [];
|
||||
|
||||
for (const wid of workers) {
|
||||
const payload = { ...base, worker_id: wid };
|
||||
|
||||
const insertId = await new Promise((resolve, reject) => {
|
||||
dailyIssueReportModel.create(payload, (err, id) => {
|
||||
if (err) reject(err);
|
||||
else resolve(id);
|
||||
});
|
||||
});
|
||||
|
||||
insertedIds.push(insertId);
|
||||
}
|
||||
|
||||
res.json({ success: true, issue_report_ids: insertedIds });
|
||||
} catch (err) {
|
||||
console.error('🔥 createDailyIssueReport error:', err);
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. READ BY DATE
|
||||
exports.getDailyIssuesByDate = async (req, res) => {
|
||||
try {
|
||||
const { date } = req.query;
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
dailyIssueReportModel.getAllByDate(date, (err, data) => {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. READ ONE
|
||||
exports.getDailyIssueById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
dailyIssueReportModel.getById(id, (err, data) => {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'DailyIssueReport not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. UPDATE
|
||||
exports.updateDailyIssue = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
dailyIssueReportModel.update(id, req.body, (err, affectedRows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(affectedRows);
|
||||
});
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'No changes or not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. DELETE
|
||||
exports.removeDailyIssue = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
dailyIssueReportModel.remove(id, (err, affectedRows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(affectedRows);
|
||||
});
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'DailyIssueReport not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
750
api.hyungi.net/controllers/dailyWorkReportController 이전.js
Normal file
750
api.hyungi.net/controllers/dailyWorkReportController 이전.js
Normal file
@@ -0,0 +1,750 @@
|
||||
// controllers/dailyWorkReportController.js - 누적입력 방식 + 모든 기존 기능 포함
|
||||
const dailyWorkReportModel = require('../models/dailyWorkReportModel');
|
||||
|
||||
/**
|
||||
* 📝 작업보고서 생성 (누적 방식 - 덮어쓰기 없음!)
|
||||
*/
|
||||
const createDailyWorkReport = (req, res) => {
|
||||
const { report_date, worker_id, work_entries } = req.body;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
const created_by_name = req.user?.name || req.user?.username || '알 수 없는 사용자';
|
||||
|
||||
// 1. 기본 유효성 검사
|
||||
if (!report_date || !worker_id || !work_entries) {
|
||||
return res.status(400).json({
|
||||
error: '필수 필드가 누락되었습니다.',
|
||||
required: ['report_date', 'worker_id', 'work_entries'],
|
||||
received: {
|
||||
report_date: !!report_date,
|
||||
worker_id: !!worker_id,
|
||||
work_entries: !!work_entries
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!Array.isArray(work_entries) || work_entries.length === 0) {
|
||||
return res.status(400).json({
|
||||
error: '최소 하나의 작업 항목이 필요합니다.',
|
||||
received_entries: work_entries?.length || 0
|
||||
});
|
||||
}
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
// 2. 작업 항목 유효성 검사
|
||||
for (let i = 0; i < work_entries.length; i++) {
|
||||
const entry = work_entries[i];
|
||||
const requiredFields = ['project_id', 'work_type_id', 'work_status_id', 'work_hours'];
|
||||
|
||||
for (const field of requiredFields) {
|
||||
if (entry[field] === undefined || entry[field] === null || entry[field] === '') {
|
||||
return res.status(400).json({
|
||||
error: `작업 항목 ${i + 1}의 ${field}가 누락되었습니다.`,
|
||||
entry_index: i,
|
||||
missing_field: field
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 에러 상태인 경우 에러 타입 필수
|
||||
if (entry.work_status_id === 2 && (!entry.error_type_id)) {
|
||||
return res.status(400).json({
|
||||
error: `작업 항목 ${i + 1}이 에러 상태인 경우 error_type_id가 필요합니다.`,
|
||||
entry_index: i
|
||||
});
|
||||
}
|
||||
|
||||
// 시간 유효성 검사
|
||||
const hours = parseFloat(entry.work_hours);
|
||||
if (isNaN(hours) || hours < 0 || hours > 24) {
|
||||
return res.status(400).json({
|
||||
error: `작업 항목 ${i + 1}의 작업시간이 유효하지 않습니다. (0-24시간)`,
|
||||
entry_index: i,
|
||||
received_hours: entry.work_hours
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 총 시간 계산
|
||||
const total_hours = work_entries.reduce((sum, entry) => sum + (parseFloat(entry.work_hours) || 0), 0);
|
||||
|
||||
// 4. 요청 데이터 구성
|
||||
const reportData = {
|
||||
report_date,
|
||||
worker_id: parseInt(worker_id),
|
||||
work_entries,
|
||||
created_by,
|
||||
created_by_name,
|
||||
total_hours,
|
||||
is_update: false
|
||||
};
|
||||
|
||||
console.log('📝 작업보고서 누적 추가 요청:', {
|
||||
date: report_date,
|
||||
worker: worker_id,
|
||||
creator: created_by_name,
|
||||
creator_id: created_by,
|
||||
entries: work_entries.length,
|
||||
total_hours
|
||||
});
|
||||
|
||||
// 5. 누적 추가 실행 (덮어쓰기 없음!)
|
||||
dailyWorkReportModel.createDailyReport(reportData, (err, result) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 생성 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 생성 중 오류가 발생했습니다.',
|
||||
details: err.message,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✅ 작업보고서 누적 추가 성공:', result);
|
||||
res.status(201).json({
|
||||
message: '작업보고서가 성공적으로 누적 추가되었습니다.',
|
||||
report_date,
|
||||
worker_id,
|
||||
created_by: created_by_name,
|
||||
timestamp: new Date().toISOString(),
|
||||
...result
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 누적 현황 조회 (새로운 기능)
|
||||
*/
|
||||
const getAccumulatedReports = (req, res) => {
|
||||
const { date, worker_id } = req.query;
|
||||
|
||||
if (!date || !worker_id) {
|
||||
return res.status(400).json({
|
||||
error: 'date와 worker_id가 필요합니다.',
|
||||
example: 'date=2024-06-16&worker_id=1'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 누적 현황 조회: date=${date}, worker_id=${worker_id}`);
|
||||
|
||||
dailyWorkReportModel.getAccumulatedReportsByDate(date, worker_id, (err, data) => {
|
||||
if (err) {
|
||||
console.error('누적 현황 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '누적 현황 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 누적 현황 조회 결과: ${data.length}개`);
|
||||
res.json({
|
||||
date,
|
||||
worker_id,
|
||||
total_entries: data.length,
|
||||
accumulated_data: data,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 기여자별 요약 조회 (새로운 기능)
|
||||
*/
|
||||
const getContributorsSummary = (req, res) => {
|
||||
const { date, worker_id } = req.query;
|
||||
|
||||
if (!date || !worker_id) {
|
||||
return res.status(400).json({
|
||||
error: 'date와 worker_id가 필요합니다.',
|
||||
example: 'date=2024-06-16&worker_id=1'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 기여자별 요약 조회: date=${date}, worker_id=${worker_id}`);
|
||||
|
||||
dailyWorkReportModel.getContributorsByDate(date, worker_id, (err, data) => {
|
||||
if (err) {
|
||||
console.error('기여자별 요약 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '기여자별 요약 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
const totalHours = data.reduce((sum, contributor) => sum + parseFloat(contributor.total_hours || 0), 0);
|
||||
|
||||
console.log(`📊 기여자별 요약: ${data.length}명, 총 ${totalHours}시간`);
|
||||
res.json({
|
||||
date,
|
||||
worker_id,
|
||||
contributors: data,
|
||||
total_contributors: data.length,
|
||||
grand_total_hours: totalHours,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 개인 누적 현황 조회 (새로운 기능)
|
||||
*/
|
||||
const getMyAccumulatedData = (req, res) => {
|
||||
const { date, worker_id } = req.query;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!date || !worker_id) {
|
||||
return res.status(400).json({
|
||||
error: 'date와 worker_id가 필요합니다.',
|
||||
example: 'date=2024-06-16&worker_id=1'
|
||||
});
|
||||
}
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 개인 누적 현황 조회: date=${date}, worker_id=${worker_id}, created_by=${created_by}`);
|
||||
|
||||
dailyWorkReportModel.getMyAccumulatedHours(date, worker_id, created_by, (err, data) => {
|
||||
if (err) {
|
||||
console.error('개인 누적 현황 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '개인 누적 현황 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 개인 누적: ${data.my_entry_count}개 항목, ${data.my_total_hours}시간`);
|
||||
res.json({
|
||||
date,
|
||||
worker_id,
|
||||
created_by,
|
||||
my_data: data,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 🗑️ 개별 항목 삭제 (본인 작성분만 - 새로운 기능)
|
||||
*/
|
||||
const removeMyEntry = (req, res) => {
|
||||
const { id } = req.params;
|
||||
const deleted_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!deleted_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🗑️ 개별 항목 삭제 요청: id=${id}, 삭제자=${deleted_by}`);
|
||||
|
||||
dailyWorkReportModel.removeSpecificEntry(id, deleted_by, (err, result) => {
|
||||
if (err) {
|
||||
console.error('개별 항목 삭제 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '항목 삭제 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ 개별 항목 삭제 완료: id=${id}`);
|
||||
res.json({
|
||||
message: '항목이 성공적으로 삭제되었습니다.',
|
||||
id: id,
|
||||
deleted_by,
|
||||
timestamp: new Date().toISOString(),
|
||||
...result
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 작업보고서 조회 (쿼리 파라미터 기반 - 작성자별 필터링 강화)
|
||||
*/
|
||||
const getDailyWorkReports = (req, res) => {
|
||||
const { date, worker_id, created_by: requested_created_by } = req.query;
|
||||
const current_user_id = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!current_user_id) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
// 일반 사용자는 자신이 작성한 것만 볼 수 있음
|
||||
const created_by = requested_created_by || current_user_id;
|
||||
|
||||
console.log('📊 작업보고서 조회 요청:', {
|
||||
date,
|
||||
worker_id,
|
||||
requested_created_by,
|
||||
current_user_id,
|
||||
final_created_by: created_by
|
||||
});
|
||||
|
||||
if (date && created_by) {
|
||||
// 날짜 + 작성자별 조회
|
||||
dailyWorkReportModel.getByDateAndCreator(date, created_by, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 날짜+작성자별 조회 결과: ${data.length}개`);
|
||||
res.json(data);
|
||||
});
|
||||
} else if (date && worker_id) {
|
||||
// 기존 방식: 날짜 + 작업자별 (하지만 작성자 필터링 추가)
|
||||
dailyWorkReportModel.getByDateAndWorker(date, worker_id, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
// 본인이 작성한 것만 필터링
|
||||
const filteredData = data.filter(report => report.created_by === current_user_id);
|
||||
console.log(`📊 날짜+작업자별 조회 결과: 전체 ${data.length}개 → 필터링 후 ${filteredData.length}개`);
|
||||
res.json(filteredData);
|
||||
});
|
||||
} else if (date) {
|
||||
// 날짜별 조회 (작성자 필터링)
|
||||
dailyWorkReportModel.getByDate(date, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
// 본인이 작성한 것만 필터링
|
||||
const filteredData = data.filter(report => report.created_by === current_user_id);
|
||||
console.log(`📊 날짜별 조회 결과: 전체 ${data.length}개 → 필터링 후 ${filteredData.length}개`);
|
||||
res.json(filteredData);
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({
|
||||
error: '날짜(date) 파라미터가 필요합니다.',
|
||||
example: 'date=2024-06-16',
|
||||
optional: ['worker_id', 'created_by']
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 날짜별 작업보고서 조회 (경로 파라미터)
|
||||
*/
|
||||
const getDailyWorkReportsByDate = (req, res) => {
|
||||
const { date } = req.params;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 날짜별 조회 (경로): date=${date}, created_by=${created_by}`);
|
||||
|
||||
dailyWorkReportModel.getByDate(date, (err, data) => {
|
||||
if (err) {
|
||||
console.error('날짜별 작업보고서 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
// 본인이 작성한 것만 필터링
|
||||
const filteredData = data.filter(report => report.created_by === created_by);
|
||||
console.log(`📊 날짜별 조회 결과: 전체 ${data.length}개 → 필터링 후 ${filteredData.length}개`);
|
||||
res.json(filteredData);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 🔍 작업보고서 검색 (페이지네이션 포함)
|
||||
*/
|
||||
const searchWorkReports = (req, res) => {
|
||||
const { start_date, end_date, worker_id, project_id, work_status_id, page = 1, limit = 20 } = req.query;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!start_date || !end_date) {
|
||||
return res.status(400).json({
|
||||
error: 'start_date와 end_date가 필요합니다.',
|
||||
example: 'start_date=2024-01-01&end_date=2024-01-31',
|
||||
optional: ['worker_id', 'project_id', 'work_status_id', 'page', 'limit']
|
||||
});
|
||||
}
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
const searchParams = {
|
||||
start_date,
|
||||
end_date,
|
||||
worker_id: worker_id ? parseInt(worker_id) : null,
|
||||
project_id: project_id ? parseInt(project_id) : null,
|
||||
work_status_id: work_status_id ? parseInt(work_status_id) : null,
|
||||
created_by, // 작성자 필터링 추가
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit)
|
||||
};
|
||||
|
||||
console.log('🔍 작업보고서 검색 요청:', searchParams);
|
||||
|
||||
dailyWorkReportModel.searchWithDetails(searchParams, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 검색 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 검색 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🔍 검색 결과: ${data.reports?.length || 0}개 (전체: ${data.total || 0}개)`);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📈 통계 조회 (작성자별 필터링)
|
||||
*/
|
||||
const getWorkReportStats = (req, res) => {
|
||||
const { start_date, end_date } = req.query;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!start_date || !end_date) {
|
||||
return res.status(400).json({
|
||||
error: 'start_date와 end_date가 필요합니다.',
|
||||
example: 'start_date=2024-01-01&end_date=2024-01-31'
|
||||
});
|
||||
}
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📈 통계 조회: ${start_date} ~ ${end_date}, 요청자: ${created_by}`);
|
||||
|
||||
dailyWorkReportModel.getStatistics(start_date, end_date, (err, data) => {
|
||||
if (err) {
|
||||
console.error('통계 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '통계 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
...data,
|
||||
metadata: {
|
||||
note: '현재는 전체 통계입니다. 개인별 통계는 추후 구현 예정',
|
||||
requested_by: created_by,
|
||||
period: `${start_date} ~ ${end_date}`,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 일일 근무 요약 조회
|
||||
*/
|
||||
const getDailySummary = (req, res) => {
|
||||
const { date, worker_id } = req.query;
|
||||
|
||||
if (date) {
|
||||
console.log(`📊 일일 요약 조회: date=${date}`);
|
||||
dailyWorkReportModel.getSummaryByDate(date, (err, data) => {
|
||||
if (err) {
|
||||
console.error('일일 요약 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '일일 요약 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
res.json(data);
|
||||
});
|
||||
} else if (worker_id) {
|
||||
console.log(`📊 작업자별 요약 조회: worker_id=${worker_id}`);
|
||||
dailyWorkReportModel.getSummaryByWorker(worker_id, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업자별 요약 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업자별 요약 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
res.json(data);
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({
|
||||
error: 'date 또는 worker_id 파라미터가 필요합니다.',
|
||||
examples: [
|
||||
'date=2024-06-16',
|
||||
'worker_id=1'
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 📅 월간 요약 조회
|
||||
*/
|
||||
const getMonthlySummary = (req, res) => {
|
||||
const { year, month } = req.query;
|
||||
|
||||
if (!year || !month) {
|
||||
return res.status(400).json({
|
||||
error: 'year와 month가 필요합니다.',
|
||||
example: 'year=2024&month=01',
|
||||
note: 'month는 01, 02, ..., 12 형식으로 입력하세요.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📅 월간 요약 조회: ${year}-${month}`);
|
||||
|
||||
dailyWorkReportModel.getMonthlySummary(year, month, (err, data) => {
|
||||
if (err) {
|
||||
console.error('월간 요약 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '월간 요약 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
year: parseInt(year),
|
||||
month: parseInt(month),
|
||||
summary: data,
|
||||
total_entries: data.length,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* ✏️ 작업보고서 수정
|
||||
*/
|
||||
const updateWorkReport = (req, res) => {
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
const updated_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!updated_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
updateData.updated_by = updated_by;
|
||||
|
||||
console.log(`✏️ 작업보고서 수정 요청: id=${id}, 수정자=${updated_by}`);
|
||||
|
||||
dailyWorkReportModel.updateById(id, updateData, (err, affectedRows) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 수정 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 수정 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
if (affectedRows === 0) {
|
||||
return res.status(404).json({
|
||||
error: '수정할 작업보고서를 찾을 수 없습니다.',
|
||||
id: id
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ 작업보고서 수정 완료: id=${id}`);
|
||||
res.json({
|
||||
message: '작업보고서가 성공적으로 수정되었습니다.',
|
||||
id: id,
|
||||
affected_rows: affectedRows,
|
||||
updated_by,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 🗑️ 특정 작업보고서 삭제
|
||||
*/
|
||||
const removeDailyWorkReport = (req, res) => {
|
||||
const { id } = req.params;
|
||||
const deleted_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!deleted_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🗑️ 작업보고서 삭제 요청: id=${id}, 삭제자=${deleted_by}`);
|
||||
|
||||
dailyWorkReportModel.removeById(id, deleted_by, (err, affectedRows) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 삭제 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 삭제 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
if (affectedRows === 0) {
|
||||
return res.status(404).json({
|
||||
error: '삭제할 작업보고서를 찾을 수 없습니다.',
|
||||
id: id
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ 작업보고서 삭제 완료: id=${id}`);
|
||||
res.json({
|
||||
message: '작업보고서가 성공적으로 삭제되었습니다.',
|
||||
id: id,
|
||||
affected_rows: affectedRows,
|
||||
deleted_by,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 🗑️ 작업자의 특정 날짜 전체 삭제
|
||||
*/
|
||||
const removeDailyWorkReportByDateAndWorker = (req, res) => {
|
||||
const { date, worker_id } = req.params;
|
||||
const deleted_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!deleted_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🗑️ 날짜+작업자별 전체 삭제 요청: date=${date}, worker_id=${worker_id}, 삭제자=${deleted_by}`);
|
||||
|
||||
dailyWorkReportModel.removeByDateAndWorker(date, worker_id, deleted_by, (err, affectedRows) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 전체 삭제 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 삭제 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
if (affectedRows === 0) {
|
||||
return res.status(404).json({
|
||||
error: '삭제할 작업보고서를 찾을 수 없습니다.',
|
||||
date: date,
|
||||
worker_id: worker_id
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ 날짜+작업자별 전체 삭제 완료: ${affectedRows}개`);
|
||||
res.json({
|
||||
message: `${date} 날짜의 작업자 ${worker_id} 작업보고서 ${affectedRows}개가 삭제되었습니다.`,
|
||||
date,
|
||||
worker_id,
|
||||
affected_rows: affectedRows,
|
||||
deleted_by,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📋 마스터 데이터 조회 함수들
|
||||
*/
|
||||
const getWorkTypes = (req, res) => {
|
||||
console.log('📋 작업 유형 조회 요청');
|
||||
dailyWorkReportModel.getAllWorkTypes((err, data) => {
|
||||
if (err) {
|
||||
console.error('작업 유형 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업 유형 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
console.log(`📋 작업 유형 조회 결과: ${data.length}개`);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
const getWorkStatusTypes = (req, res) => {
|
||||
console.log('📋 업무 상태 유형 조회 요청');
|
||||
dailyWorkReportModel.getAllWorkStatusTypes((err, data) => {
|
||||
if (err) {
|
||||
console.error('업무 상태 유형 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '업무 상태 유형 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
console.log(`📋 업무 상태 유형 조회 결과: ${data.length}개`);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
const getErrorTypes = (req, res) => {
|
||||
console.log('📋 에러 유형 조회 요청');
|
||||
dailyWorkReportModel.getAllErrorTypes((err, data) => {
|
||||
if (err) {
|
||||
console.error('에러 유형 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '에러 유형 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
console.log(`📋 에러 유형 조회 결과: ${data.length}개`);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
// 모든 컨트롤러 함수 내보내기 (기존 기능 + 누적 기능)
|
||||
module.exports = {
|
||||
// 📝 핵심 CRUD 함수들
|
||||
createDailyWorkReport, // 누적 추가 (덮어쓰기 없음)
|
||||
getDailyWorkReports, // 조회 (작성자별 필터링)
|
||||
getDailyWorkReportsByDate, // 날짜별 조회
|
||||
searchWorkReports, // 검색 (페이지네이션)
|
||||
updateWorkReport, // 수정
|
||||
removeDailyWorkReport, // 개별 삭제
|
||||
removeDailyWorkReportByDateAndWorker, // 전체 삭제
|
||||
|
||||
// 🔄 누적 관련 새로운 함수들
|
||||
getAccumulatedReports, // 누적 현황 조회
|
||||
getContributorsSummary, // 기여자별 요약
|
||||
getMyAccumulatedData, // 개인 누적 현황
|
||||
removeMyEntry, // 개별 항목 삭제 (본인 것만)
|
||||
|
||||
// 📊 요약 및 통계 함수들
|
||||
getDailySummary, // 일일 요약
|
||||
getMonthlySummary, // 월간 요약
|
||||
getWorkReportStats, // 통계
|
||||
|
||||
// 📋 마스터 데이터 함수들
|
||||
getWorkTypes, // 작업 유형 목록
|
||||
getWorkStatusTypes, // 업무 상태 유형 목록
|
||||
getErrorTypes // 에러 유형 목록
|
||||
};
|
||||
789
api.hyungi.net/controllers/dailyWorkReportController.js
Normal file
789
api.hyungi.net/controllers/dailyWorkReportController.js
Normal file
@@ -0,0 +1,789 @@
|
||||
// controllers/dailyWorkReportController.js - 권한별 전체 조회 지원 버전
|
||||
const dailyWorkReportModel = require('../models/dailyWorkReportModel');
|
||||
|
||||
/**
|
||||
* 📝 작업보고서 생성 (누적 방식 - 덮어쓰기 없음!)
|
||||
*/
|
||||
const createDailyWorkReport = (req, res) => {
|
||||
const { report_date, worker_id, work_entries } = req.body;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
const created_by_name = req.user?.name || req.user?.username || '알 수 없는 사용자';
|
||||
|
||||
// 1. 기본 유효성 검사
|
||||
if (!report_date || !worker_id || !work_entries) {
|
||||
return res.status(400).json({
|
||||
error: '필수 필드가 누락되었습니다.',
|
||||
required: ['report_date', 'worker_id', 'work_entries'],
|
||||
received: {
|
||||
report_date: !!report_date,
|
||||
worker_id: !!worker_id,
|
||||
work_entries: !!work_entries
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!Array.isArray(work_entries) || work_entries.length === 0) {
|
||||
return res.status(400).json({
|
||||
error: '최소 하나의 작업 항목이 필요합니다.',
|
||||
received_entries: work_entries?.length || 0
|
||||
});
|
||||
}
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
// 2. 작업 항목 유효성 검사
|
||||
for (let i = 0; i < work_entries.length; i++) {
|
||||
const entry = work_entries[i];
|
||||
const requiredFields = ['project_id', 'work_type_id', 'work_status_id', 'work_hours'];
|
||||
|
||||
for (const field of requiredFields) {
|
||||
if (entry[field] === undefined || entry[field] === null || entry[field] === '') {
|
||||
return res.status(400).json({
|
||||
error: `작업 항목 ${i + 1}의 ${field}가 누락되었습니다.`,
|
||||
entry_index: i,
|
||||
missing_field: field
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 에러 상태인 경우 에러 타입 필수
|
||||
if (entry.work_status_id === 2 && (!entry.error_type_id)) {
|
||||
return res.status(400).json({
|
||||
error: `작업 항목 ${i + 1}이 에러 상태인 경우 error_type_id가 필요합니다.`,
|
||||
entry_index: i
|
||||
});
|
||||
}
|
||||
|
||||
// 시간 유효성 검사
|
||||
const hours = parseFloat(entry.work_hours);
|
||||
if (isNaN(hours) || hours < 0 || hours > 24) {
|
||||
return res.status(400).json({
|
||||
error: `작업 항목 ${i + 1}의 작업시간이 유효하지 않습니다. (0-24시간)`,
|
||||
entry_index: i,
|
||||
received_hours: entry.work_hours
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 총 시간 계산
|
||||
const total_hours = work_entries.reduce((sum, entry) => sum + (parseFloat(entry.work_hours) || 0), 0);
|
||||
|
||||
// 4. 요청 데이터 구성
|
||||
const reportData = {
|
||||
report_date,
|
||||
worker_id: parseInt(worker_id),
|
||||
work_entries,
|
||||
created_by,
|
||||
created_by_name,
|
||||
total_hours,
|
||||
is_update: false
|
||||
};
|
||||
|
||||
console.log('📝 작업보고서 누적 추가 요청:', {
|
||||
date: report_date,
|
||||
worker: worker_id,
|
||||
creator: created_by_name,
|
||||
creator_id: created_by,
|
||||
entries: work_entries.length,
|
||||
total_hours
|
||||
});
|
||||
|
||||
// 5. 누적 추가 실행 (덮어쓰기 없음!)
|
||||
dailyWorkReportModel.createDailyReport(reportData, (err, result) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 생성 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 생성 중 오류가 발생했습니다.',
|
||||
details: err.message,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
console.log('✅ 작업보고서 누적 추가 성공:', result);
|
||||
res.status(201).json({
|
||||
message: '작업보고서가 성공적으로 누적 추가되었습니다.',
|
||||
report_date,
|
||||
worker_id,
|
||||
created_by: created_by_name,
|
||||
timestamp: new Date().toISOString(),
|
||||
...result
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 누적 현황 조회 (새로운 기능)
|
||||
*/
|
||||
const getAccumulatedReports = (req, res) => {
|
||||
const { date, worker_id } = req.query;
|
||||
|
||||
if (!date || !worker_id) {
|
||||
return res.status(400).json({
|
||||
error: 'date와 worker_id가 필요합니다.',
|
||||
example: 'date=2024-06-16&worker_id=1'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 누적 현황 조회: date=${date}, worker_id=${worker_id}`);
|
||||
|
||||
dailyWorkReportModel.getAccumulatedReportsByDate(date, worker_id, (err, data) => {
|
||||
if (err) {
|
||||
console.error('누적 현황 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '누적 현황 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 누적 현황 조회 결과: ${data.length}개`);
|
||||
res.json({
|
||||
date,
|
||||
worker_id,
|
||||
total_entries: data.length,
|
||||
accumulated_data: data,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 기여자별 요약 조회 (새로운 기능)
|
||||
*/
|
||||
const getContributorsSummary = (req, res) => {
|
||||
const { date, worker_id } = req.query;
|
||||
|
||||
if (!date || !worker_id) {
|
||||
return res.status(400).json({
|
||||
error: 'date와 worker_id가 필요합니다.',
|
||||
example: 'date=2024-06-16&worker_id=1'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 기여자별 요약 조회: date=${date}, worker_id=${worker_id}`);
|
||||
|
||||
dailyWorkReportModel.getContributorsByDate(date, worker_id, (err, data) => {
|
||||
if (err) {
|
||||
console.error('기여자별 요약 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '기여자별 요약 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
const totalHours = data.reduce((sum, contributor) => sum + parseFloat(contributor.total_hours || 0), 0);
|
||||
|
||||
console.log(`📊 기여자별 요약: ${data.length}명, 총 ${totalHours}시간`);
|
||||
res.json({
|
||||
date,
|
||||
worker_id,
|
||||
contributors: data,
|
||||
total_contributors: data.length,
|
||||
grand_total_hours: totalHours,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 개인 누적 현황 조회 (새로운 기능)
|
||||
*/
|
||||
const getMyAccumulatedData = (req, res) => {
|
||||
const { date, worker_id } = req.query;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!date || !worker_id) {
|
||||
return res.status(400).json({
|
||||
error: 'date와 worker_id가 필요합니다.',
|
||||
example: 'date=2024-06-16&worker_id=1'
|
||||
});
|
||||
}
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 개인 누적 현황 조회: date=${date}, worker_id=${worker_id}, created_by=${created_by}`);
|
||||
|
||||
dailyWorkReportModel.getMyAccumulatedHours(date, worker_id, created_by, (err, data) => {
|
||||
if (err) {
|
||||
console.error('개인 누적 현황 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '개인 누적 현황 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 개인 누적: ${data.my_entry_count}개 항목, ${data.my_total_hours}시간`);
|
||||
res.json({
|
||||
date,
|
||||
worker_id,
|
||||
created_by,
|
||||
my_data: data,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 🗑️ 개별 항목 삭제 (본인 작성분만 - 새로운 기능)
|
||||
*/
|
||||
const removeMyEntry = (req, res) => {
|
||||
const { id } = req.params;
|
||||
const deleted_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!deleted_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🗑️ 개별 항목 삭제 요청: id=${id}, 삭제자=${deleted_by}`);
|
||||
|
||||
dailyWorkReportModel.removeSpecificEntry(id, deleted_by, (err, result) => {
|
||||
if (err) {
|
||||
console.error('개별 항목 삭제 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '항목 삭제 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ 개별 항목 삭제 완료: id=${id}`);
|
||||
res.json({
|
||||
message: '항목이 성공적으로 삭제되었습니다.',
|
||||
id: id,
|
||||
deleted_by,
|
||||
timestamp: new Date().toISOString(),
|
||||
...result
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 작업보고서 조회 (권한별 전체 조회 지원 - 핵심 수정!)
|
||||
*/
|
||||
const getDailyWorkReports = (req, res) => {
|
||||
const { date, worker_id, created_by: requested_created_by, view_all, admin, all, no_filter, ignore_created_by } = req.query;
|
||||
const current_user_id = req.user?.user_id || req.user?.id;
|
||||
const user_access_level = req.user?.access_level;
|
||||
|
||||
if (!current_user_id) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
// 🎯 권한별 필터링 로직 개선
|
||||
const isAdmin = user_access_level === 'system' || user_access_level === 'admin';
|
||||
const hasViewAllFlag = view_all === 'true' || admin === 'true' || all === 'true' ||
|
||||
no_filter === 'true' || ignore_created_by === 'true' ||
|
||||
requested_created_by === 'all' || requested_created_by === '';
|
||||
|
||||
const canViewAll = isAdmin || hasViewAllFlag;
|
||||
|
||||
// 관리자가 아니고 전체 조회 플래그도 없으면 본인 작성분으로 제한
|
||||
let final_created_by = null;
|
||||
if (!canViewAll) {
|
||||
final_created_by = requested_created_by || current_user_id;
|
||||
} else if (requested_created_by && requested_created_by !== 'all' && requested_created_by !== '') {
|
||||
final_created_by = requested_created_by;
|
||||
}
|
||||
|
||||
console.log('📊 작업보고서 조회 요청:', {
|
||||
date,
|
||||
worker_id,
|
||||
requested_created_by,
|
||||
current_user_id,
|
||||
user_access_level,
|
||||
isAdmin,
|
||||
hasViewAllFlag,
|
||||
canViewAll,
|
||||
final_created_by
|
||||
});
|
||||
|
||||
if (date && final_created_by) {
|
||||
// 날짜 + 작성자별 조회
|
||||
dailyWorkReportModel.getByDateAndCreator(date, final_created_by, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 날짜+작성자별 조회 결과: ${data.length}개`);
|
||||
res.json(data);
|
||||
});
|
||||
} else if (date && worker_id) {
|
||||
// 날짜 + 작업자별 조회
|
||||
dailyWorkReportModel.getByDateAndWorker(date, worker_id, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
// 🎯 권한별 필터링
|
||||
let finalData = data;
|
||||
if (!canViewAll) {
|
||||
finalData = data.filter(report => report.created_by === current_user_id);
|
||||
console.log(`📊 권한 필터링: 전체 ${data.length}개 → ${finalData.length}개`);
|
||||
} else {
|
||||
console.log(`📊 관리자/전체 조회 권한: ${data.length}개 전체 반환`);
|
||||
}
|
||||
|
||||
res.json(finalData);
|
||||
});
|
||||
} else if (date) {
|
||||
// 날짜별 조회
|
||||
dailyWorkReportModel.getByDate(date, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
// 🎯 권한별 필터링
|
||||
let finalData = data;
|
||||
if (!canViewAll) {
|
||||
finalData = data.filter(report => report.created_by === current_user_id);
|
||||
console.log(`📊 권한 필터링: 전체 ${data.length}개 → ${finalData.length}개`);
|
||||
} else {
|
||||
console.log(`📊 관리자/전체 조회 권한: ${data.length}개 전체 반환`);
|
||||
}
|
||||
|
||||
res.json(finalData);
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({
|
||||
error: '날짜(date) 파라미터가 필요합니다.',
|
||||
example: 'date=2024-06-16',
|
||||
optional: ['worker_id', 'created_by', 'view_all', 'admin', 'all']
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 날짜별 작업보고서 조회 (경로 파라미터 - 권한별 전체 조회 지원)
|
||||
*/
|
||||
const getDailyWorkReportsByDate = (req, res) => {
|
||||
const { date } = req.params;
|
||||
const current_user_id = req.user?.user_id || req.user?.id;
|
||||
const user_access_level = req.user?.access_level;
|
||||
|
||||
if (!current_user_id) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
const isAdmin = user_access_level === 'system' || user_access_level === 'admin';
|
||||
|
||||
console.log(`📊 날짜별 조회 (경로): date=${date}, user=${current_user_id}, 권한=${user_access_level}, 관리자=${isAdmin}`);
|
||||
|
||||
dailyWorkReportModel.getByDate(date, (err, data) => {
|
||||
if (err) {
|
||||
console.error('날짜별 작업보고서 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
// 🎯 권한별 필터링
|
||||
let finalData = data;
|
||||
if (!isAdmin) {
|
||||
finalData = data.filter(report => report.created_by === current_user_id);
|
||||
console.log(`📊 권한 필터링: 전체 ${data.length}개 → ${finalData.length}개`);
|
||||
} else {
|
||||
console.log(`📊 관리자 권한으로 전체 조회: ${data.length}개`);
|
||||
}
|
||||
|
||||
res.json(finalData);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 🔍 작업보고서 검색 (페이지네이션 포함)
|
||||
*/
|
||||
const searchWorkReports = (req, res) => {
|
||||
const { start_date, end_date, worker_id, project_id, work_status_id, page = 1, limit = 20 } = req.query;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!start_date || !end_date) {
|
||||
return res.status(400).json({
|
||||
error: 'start_date와 end_date가 필요합니다.',
|
||||
example: 'start_date=2024-01-01&end_date=2024-01-31',
|
||||
optional: ['worker_id', 'project_id', 'work_status_id', 'page', 'limit']
|
||||
});
|
||||
}
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
const searchParams = {
|
||||
start_date,
|
||||
end_date,
|
||||
worker_id: worker_id ? parseInt(worker_id) : null,
|
||||
project_id: project_id ? parseInt(project_id) : null,
|
||||
work_status_id: work_status_id ? parseInt(work_status_id) : null,
|
||||
created_by, // 작성자 필터링 추가
|
||||
page: parseInt(page),
|
||||
limit: parseInt(limit)
|
||||
};
|
||||
|
||||
console.log('🔍 작업보고서 검색 요청:', searchParams);
|
||||
|
||||
dailyWorkReportModel.searchWithDetails(searchParams, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 검색 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 검색 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🔍 검색 결과: ${data.reports?.length || 0}개 (전체: ${data.total || 0}개)`);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📈 통계 조회 (작성자별 필터링)
|
||||
*/
|
||||
const getWorkReportStats = (req, res) => {
|
||||
const { start_date, end_date } = req.query;
|
||||
const created_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!start_date || !end_date) {
|
||||
return res.status(400).json({
|
||||
error: 'start_date와 end_date가 필요합니다.',
|
||||
example: 'start_date=2024-01-01&end_date=2024-01-31'
|
||||
});
|
||||
}
|
||||
|
||||
if (!created_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📈 통계 조회: ${start_date} ~ ${end_date}, 요청자: ${created_by}`);
|
||||
|
||||
dailyWorkReportModel.getStatistics(start_date, end_date, (err, data) => {
|
||||
if (err) {
|
||||
console.error('통계 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '통계 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
...data,
|
||||
metadata: {
|
||||
note: '현재는 전체 통계입니다. 개인별 통계는 추후 구현 예정',
|
||||
requested_by: created_by,
|
||||
period: `${start_date} ~ ${end_date}`,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📊 일일 근무 요약 조회
|
||||
*/
|
||||
const getDailySummary = (req, res) => {
|
||||
const { date, worker_id } = req.query;
|
||||
|
||||
if (date) {
|
||||
console.log(`📊 일일 요약 조회: date=${date}`);
|
||||
dailyWorkReportModel.getSummaryByDate(date, (err, data) => {
|
||||
if (err) {
|
||||
console.error('일일 요약 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '일일 요약 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
res.json(data);
|
||||
});
|
||||
} else if (worker_id) {
|
||||
console.log(`📊 작업자별 요약 조회: worker_id=${worker_id}`);
|
||||
dailyWorkReportModel.getSummaryByWorker(worker_id, (err, data) => {
|
||||
if (err) {
|
||||
console.error('작업자별 요약 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업자별 요약 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
res.json(data);
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({
|
||||
error: 'date 또는 worker_id 파라미터가 필요합니다.',
|
||||
examples: [
|
||||
'date=2024-06-16',
|
||||
'worker_id=1'
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 📅 월간 요약 조회
|
||||
*/
|
||||
const getMonthlySummary = (req, res) => {
|
||||
const { year, month } = req.query;
|
||||
|
||||
if (!year || !month) {
|
||||
return res.status(400).json({
|
||||
error: 'year와 month가 필요합니다.',
|
||||
example: 'year=2024&month=01',
|
||||
note: 'month는 01, 02, ..., 12 형식으로 입력하세요.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📅 월간 요약 조회: ${year}-${month}`);
|
||||
|
||||
dailyWorkReportModel.getMonthlySummary(year, month, (err, data) => {
|
||||
if (err) {
|
||||
console.error('월간 요약 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '월간 요약 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
year: parseInt(year),
|
||||
month: parseInt(month),
|
||||
summary: data,
|
||||
total_entries: data.length,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* ✏️ 작업보고서 수정
|
||||
*/
|
||||
const updateWorkReport = (req, res) => {
|
||||
const { id } = req.params;
|
||||
const updateData = req.body;
|
||||
const updated_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!updated_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
updateData.updated_by = updated_by;
|
||||
|
||||
console.log(`✏️ 작업보고서 수정 요청: id=${id}, 수정자=${updated_by}`);
|
||||
|
||||
dailyWorkReportModel.updateById(id, updateData, (err, affectedRows) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 수정 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 수정 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
if (affectedRows === 0) {
|
||||
return res.status(404).json({
|
||||
error: '수정할 작업보고서를 찾을 수 없습니다.',
|
||||
id: id
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ 작업보고서 수정 완료: id=${id}`);
|
||||
res.json({
|
||||
message: '작업보고서가 성공적으로 수정되었습니다.',
|
||||
id: id,
|
||||
affected_rows: affectedRows,
|
||||
updated_by,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 🗑️ 특정 작업보고서 삭제
|
||||
*/
|
||||
const removeDailyWorkReport = (req, res) => {
|
||||
const { id } = req.params;
|
||||
const deleted_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!deleted_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🗑️ 작업보고서 삭제 요청: id=${id}, 삭제자=${deleted_by}`);
|
||||
|
||||
dailyWorkReportModel.removeById(id, deleted_by, (err, affectedRows) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 삭제 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 삭제 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
if (affectedRows === 0) {
|
||||
return res.status(404).json({
|
||||
error: '삭제할 작업보고서를 찾을 수 없습니다.',
|
||||
id: id
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ 작업보고서 삭제 완료: id=${id}`);
|
||||
res.json({
|
||||
message: '작업보고서가 성공적으로 삭제되었습니다.',
|
||||
id: id,
|
||||
affected_rows: affectedRows,
|
||||
deleted_by,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 🗑️ 작업자의 특정 날짜 전체 삭제
|
||||
*/
|
||||
const removeDailyWorkReportByDateAndWorker = (req, res) => {
|
||||
const { date, worker_id } = req.params;
|
||||
const deleted_by = req.user?.user_id || req.user?.id;
|
||||
|
||||
if (!deleted_by) {
|
||||
return res.status(401).json({
|
||||
error: '사용자 인증 정보가 없습니다.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`🗑️ 날짜+작업자별 전체 삭제 요청: date=${date}, worker_id=${worker_id}, 삭제자=${deleted_by}`);
|
||||
|
||||
dailyWorkReportModel.removeByDateAndWorker(date, worker_id, deleted_by, (err, affectedRows) => {
|
||||
if (err) {
|
||||
console.error('작업보고서 전체 삭제 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업보고서 삭제 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
|
||||
if (affectedRows === 0) {
|
||||
return res.status(404).json({
|
||||
error: '삭제할 작업보고서를 찾을 수 없습니다.',
|
||||
date: date,
|
||||
worker_id: worker_id
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`✅ 날짜+작업자별 전체 삭제 완료: ${affectedRows}개`);
|
||||
res.json({
|
||||
message: `${date} 날짜의 작업자 ${worker_id} 작업보고서 ${affectedRows}개가 삭제되었습니다.`,
|
||||
date,
|
||||
worker_id,
|
||||
affected_rows: affectedRows,
|
||||
deleted_by,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 📋 마스터 데이터 조회 함수들
|
||||
*/
|
||||
const getWorkTypes = (req, res) => {
|
||||
console.log('📋 작업 유형 조회 요청');
|
||||
dailyWorkReportModel.getAllWorkTypes((err, data) => {
|
||||
if (err) {
|
||||
console.error('작업 유형 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '작업 유형 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
console.log(`📋 작업 유형 조회 결과: ${data.length}개`);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
const getWorkStatusTypes = (req, res) => {
|
||||
console.log('📋 업무 상태 유형 조회 요청');
|
||||
dailyWorkReportModel.getAllWorkStatusTypes((err, data) => {
|
||||
if (err) {
|
||||
console.error('업무 상태 유형 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '업무 상태 유형 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
console.log(`📋 업무 상태 유형 조회 결과: ${data.length}개`);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
const getErrorTypes = (req, res) => {
|
||||
console.log('📋 에러 유형 조회 요청');
|
||||
dailyWorkReportModel.getAllErrorTypes((err, data) => {
|
||||
if (err) {
|
||||
console.error('에러 유형 조회 오류:', err);
|
||||
return res.status(500).json({
|
||||
error: '에러 유형 조회 중 오류가 발생했습니다.',
|
||||
details: err.message
|
||||
});
|
||||
}
|
||||
console.log(`📋 에러 유형 조회 결과: ${data.length}개`);
|
||||
res.json(data);
|
||||
});
|
||||
};
|
||||
|
||||
// 모든 컨트롤러 함수 내보내기 (권한별 조회 지원)
|
||||
module.exports = {
|
||||
// 📝 핵심 CRUD 함수들 (권한별 전체 조회 지원)
|
||||
createDailyWorkReport, // 누적 추가 (덮어쓰기 없음)
|
||||
getDailyWorkReports, // 조회 (권한별 필터링 개선)
|
||||
getDailyWorkReportsByDate, // 날짜별 조회 (권한별 필터링 개선)
|
||||
searchWorkReports, // 검색 (페이지네이션)
|
||||
updateWorkReport, // 수정
|
||||
removeDailyWorkReport, // 개별 삭제
|
||||
removeDailyWorkReportByDateAndWorker, // 전체 삭제
|
||||
|
||||
// 🔄 누적 관련 새로운 함수들
|
||||
getAccumulatedReports, // 누적 현황 조회
|
||||
getContributorsSummary, // 기여자별 요약
|
||||
getMyAccumulatedData, // 개인 누적 현황
|
||||
removeMyEntry, // 개별 항목 삭제 (본인 것만)
|
||||
|
||||
// 📊 요약 및 통계 함수들
|
||||
getDailySummary, // 일일 요약
|
||||
getMonthlySummary, // 월간 요약
|
||||
getWorkReportStats, // 통계
|
||||
|
||||
// 📋 마스터 데이터 함수들
|
||||
getWorkTypes, // 작업 유형 목록
|
||||
getWorkStatusTypes, // 업무 상태 유형 목록
|
||||
getErrorTypes // 에러 유형 목록
|
||||
};
|
||||
80
api.hyungi.net/controllers/equipmentListController.js
Normal file
80
api.hyungi.net/controllers/equipmentListController.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// controllers/equipmentListController.js
|
||||
const equipmentListModel = require('../models/equipmentListModel');
|
||||
|
||||
// 1. 등록
|
||||
exports.createEquipment = async (req, res) => {
|
||||
try {
|
||||
const equipmentData = req.body;
|
||||
const id = await new Promise((resolve, reject) => {
|
||||
equipmentListModel.create(equipmentData, (err, insertId) =>
|
||||
err ? reject(err) : resolve(insertId)
|
||||
);
|
||||
});
|
||||
res.json({ success: true, equipment_id: id });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 전체 조회
|
||||
exports.getAllEquipment = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
equipmentListModel.getAll((err, data) =>
|
||||
err ? reject(err) : resolve(data)
|
||||
);
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 단일 조회
|
||||
exports.getEquipmentById = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.equipment_id, 10);
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
equipmentListModel.getById(id, (err, data) =>
|
||||
err ? reject(err) : resolve(data)
|
||||
);
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'Equipment not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 수정
|
||||
exports.updateEquipment = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.equipment_id, 10);
|
||||
const data = { ...req.body, equipment_id: id };
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
equipmentListModel.update(data, (err, affectedRows) =>
|
||||
err ? reject(err) : resolve(affectedRows)
|
||||
);
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'No changes or not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 삭제
|
||||
exports.removeEquipment = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.equipment_id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
equipmentListModel.remove(id, (err, affectedRows) =>
|
||||
err ? reject(err) : resolve(affectedRows)
|
||||
);
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Equipment not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
80
api.hyungi.net/controllers/factoryInfoController.js
Normal file
80
api.hyungi.net/controllers/factoryInfoController.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// controllers/factoryInfoController.js
|
||||
const factoryInfoModel = require('../models/factoryInfoModel');
|
||||
|
||||
// 1. 공장 정보 생성
|
||||
exports.createFactoryInfo = async (req, res) => {
|
||||
try {
|
||||
const factoryData = req.body;
|
||||
const id = await new Promise((resolve, reject) => {
|
||||
factoryInfoModel.create(factoryData, (err, insertId) =>
|
||||
err ? reject(err) : resolve(insertId)
|
||||
);
|
||||
});
|
||||
res.json({ success: true, factory_id: id });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 전체 공장 정보 조회
|
||||
exports.getAllFactoryInfo = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
factoryInfoModel.getAll((err, data) =>
|
||||
err ? reject(err) : resolve(data)
|
||||
);
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 단일 조회
|
||||
exports.getFactoryInfoById = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.factory_id, 10);
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
factoryInfoModel.getById(id, (err, data) =>
|
||||
err ? reject(err) : resolve(data)
|
||||
);
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'FactoryInfo not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 수정
|
||||
exports.updateFactoryInfo = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.factory_id, 10);
|
||||
const data = { ...req.body, factory_id: id };
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
factoryInfoModel.update(data, (err, affectedRows) =>
|
||||
err ? reject(err) : resolve(affectedRows)
|
||||
);
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'No changes or not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 삭제
|
||||
exports.removeFactoryInfo = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.factory_id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
factoryInfoModel.remove(id, (err, affectedRows) =>
|
||||
err ? reject(err) : resolve(affectedRows)
|
||||
);
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'FactoryInfo not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
55
api.hyungi.net/controllers/issueTypeController.js
Normal file
55
api.hyungi.net/controllers/issueTypeController.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const issueTypeModel = require('../models/issueTypeModel');
|
||||
|
||||
exports.createIssueType = async (req, res) => {
|
||||
try {
|
||||
const id = await new Promise((resolve, reject) => {
|
||||
issueTypeModel.create(req.body, (err, insertId) =>
|
||||
err ? reject(err) : resolve(insertId)
|
||||
);
|
||||
});
|
||||
res.json({ success: true, issue_type_id: id });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
exports.getAllIssueTypes = async (_req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
issueTypeModel.getAll((err, data) => err ? reject(err) : resolve(data));
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
exports.updateIssueType = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
issueTypeModel.update(id, req.body, (err, affectedRows) =>
|
||||
err ? reject(err) : resolve(affectedRows)
|
||||
);
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Not found or no changes' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
exports.removeIssueType = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
issueTypeModel.remove(id, (err, affectedRows) =>
|
||||
err ? reject(err) : resolve(affectedRows)
|
||||
);
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
25
api.hyungi.net/controllers/pingController.js
Normal file
25
api.hyungi.net/controllers/pingController.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// controllers/pingController.js
|
||||
const { getDb } = require('../dbPool');
|
||||
const pingModel = require('../models/pingModel');
|
||||
|
||||
exports.ping = async (req, res) => {
|
||||
const data = pingModel.ping();
|
||||
try {
|
||||
// DB 연결 테스트
|
||||
const db = await getDb();
|
||||
await db.query('SELECT 1');
|
||||
return res.json({
|
||||
success: true,
|
||||
...data,
|
||||
db: 'ok'
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[PING ERROR]', err);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'db error',
|
||||
timestamp: data.timestamp,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
};
|
||||
127
api.hyungi.net/controllers/pipeSpecController.js
Normal file
127
api.hyungi.net/controllers/pipeSpecController.js
Normal file
@@ -0,0 +1,127 @@
|
||||
const { getDb } = require('../dbPool');
|
||||
|
||||
// ✅ 전체 스펙 목록 (프론트 드롭다운용 label 포함)
|
||||
exports.getAll = async (req, res) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const [rows] = await db.query(`
|
||||
SELECT spec_id, material, diameter_in, schedule
|
||||
FROM PipeSpecs
|
||||
ORDER BY material, diameter_in
|
||||
`);
|
||||
|
||||
const result = rows.map(row => ({
|
||||
spec_id: row.spec_id,
|
||||
label: `${row.material} / ${row.diameter_in} / ${row.schedule}`
|
||||
}));
|
||||
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
console.error('[getAll 오류]', err);
|
||||
res.status(500).json({ error: '파이프 스펙 전체 조회 실패' });
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ 등록
|
||||
exports.create = async (req, res) => {
|
||||
try {
|
||||
const { material, diameter_in, schedule } = req.body;
|
||||
if (!material || !diameter_in || !schedule) {
|
||||
return res.status(400).json({ error: '모든 항목이 필요합니다.' });
|
||||
}
|
||||
|
||||
const db = await getDb();
|
||||
|
||||
// 중복 체크
|
||||
const [existing] = await db.query(
|
||||
`SELECT * FROM PipeSpecs WHERE material = ? AND diameter_in = ? AND schedule = ?`,
|
||||
[material.trim(), diameter_in.trim(), schedule.trim()]
|
||||
);
|
||||
|
||||
if (existing.length > 0) {
|
||||
return res.status(409).json({ error: '이미 등록된 스펙입니다.' });
|
||||
}
|
||||
|
||||
await db.query(
|
||||
`INSERT INTO PipeSpecs (material, diameter_in, schedule) VALUES (?, ?, ?)`,
|
||||
[material.trim(), diameter_in.trim(), schedule.trim()]
|
||||
);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
console.error('[create 오류]', err);
|
||||
res.status(500).json({ error: '파이프 스펙 등록 실패' });
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ 삭제
|
||||
exports.remove = async (req, res) => {
|
||||
const { spec_id } = req.params;
|
||||
try {
|
||||
const db = await getDb();
|
||||
const [result] = await db.query(
|
||||
`DELETE FROM PipeSpecs WHERE spec_id = ?`,
|
||||
[spec_id]
|
||||
);
|
||||
if (result.affectedRows === 0) {
|
||||
return res.status(404).json({ error: '해당 스펙이 존재하지 않습니다.' });
|
||||
}
|
||||
res.json({ success: true });
|
||||
} catch (err) {
|
||||
console.error('[remove 오류]', err);
|
||||
res.status(500).json({ error: '파이프 스펙 삭제 실패' });
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ 재질 목록
|
||||
exports.getMaterials = async (req, res) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const [rows] = await db.query(
|
||||
`SELECT DISTINCT material FROM PipeSpecs ORDER BY material`
|
||||
);
|
||||
res.json(rows.map(row => row.material));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: '재질 목록 조회 실패' });
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ 직경 목록 (material 기준)
|
||||
exports.getDiameters = async (req, res) => {
|
||||
const { material } = req.query;
|
||||
if (!material) {
|
||||
return res.status(400).json({ error: 'material 파라미터가 필요합니다.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const db = await getDb();
|
||||
const [rows] = await db.query(
|
||||
`SELECT DISTINCT diameter_in FROM PipeSpecs WHERE material = ? ORDER BY diameter_in`,
|
||||
[material]
|
||||
);
|
||||
res.json(rows.map(row => row.diameter_in));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: '직경 목록 조회 실패' });
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ 스케줄 목록 (material + 직경 기준)
|
||||
exports.getSchedules = async (req, res) => {
|
||||
const { material, diameter_in } = req.query;
|
||||
if (!material || !diameter_in) {
|
||||
return res.status(400).json({ error: 'material과 diameter_in이 필요합니다.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const db = await getDb();
|
||||
const [rows] = await db.query(
|
||||
`SELECT DISTINCT schedule FROM PipeSpecs
|
||||
WHERE material = ? AND diameter_in = ?
|
||||
ORDER BY schedule`,
|
||||
[material, diameter_in]
|
||||
);
|
||||
res.json(rows.map(row => row.schedule));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: '스케줄 목록 조회 실패' });
|
||||
}
|
||||
};
|
||||
100
api.hyungi.net/controllers/processController.js
Normal file
100
api.hyungi.net/controllers/processController.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const processModel = require('../models/processModel');
|
||||
const projectModel = require('../models/projectModel');
|
||||
|
||||
// 1. 공정 등록
|
||||
exports.createProcess = async (req, res) => {
|
||||
try {
|
||||
const processData = req.body;
|
||||
|
||||
if (!processData.process_end) {
|
||||
const project = await new Promise((resolve, reject) => {
|
||||
projectModel.getById(processData.project_id, (err, row) => {
|
||||
if (err) return reject(err);
|
||||
if (!row) return reject({ status: 404, message: 'Project not found' });
|
||||
resolve(row);
|
||||
});
|
||||
});
|
||||
|
||||
processData.process_end = project.due_date;
|
||||
}
|
||||
|
||||
const lastID = await new Promise((resolve, reject) => {
|
||||
processModel.create(processData, (err, id) => (err ? reject(err) : resolve(id)));
|
||||
});
|
||||
|
||||
res.json({ success: true, process_id: lastID });
|
||||
} catch (err) {
|
||||
const status = err.status || 500;
|
||||
res.status(status).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 전체 조회
|
||||
exports.getAllProcesses = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
processModel.getAll((err, data) => (err ? reject(err) : resolve(data)));
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 단일 조회
|
||||
exports.getProcessById = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.process_id, 10);
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
processModel.getById(id, (err, data) => (err ? reject(err) : resolve(data)));
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'Process not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 수정
|
||||
exports.updateProcess = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.process_id, 10);
|
||||
const processData = { ...req.body, process_id: id };
|
||||
|
||||
if (!processData.process_end) {
|
||||
const project = await new Promise((resolve, reject) => {
|
||||
projectModel.getById(processData.project_id, (err, row) => {
|
||||
if (err) return reject(err);
|
||||
if (!row) return reject({ status: 404, message: 'Project not found' });
|
||||
resolve(row);
|
||||
});
|
||||
});
|
||||
|
||||
processData.process_end = project.due_date;
|
||||
}
|
||||
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
processModel.update(processData, (err, ch) => (err ? reject(err) : resolve(ch)));
|
||||
});
|
||||
|
||||
if (changes === 0) return res.status(404).json({ error: 'No changes or not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
const status = err.status || 500;
|
||||
res.status(status).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 삭제
|
||||
exports.removeProcess = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.process_id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
processModel.remove(id, (err, ch) => (err ? reject(err) : resolve(ch)));
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Process not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
69
api.hyungi.net/controllers/projectController.js
Normal file
69
api.hyungi.net/controllers/projectController.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const projectModel = require('../models/projectModel');
|
||||
|
||||
// 1. 프로젝트 생성
|
||||
exports.createProject = async (req, res) => {
|
||||
try {
|
||||
const projectData = req.body;
|
||||
const id = await new Promise((resolve, reject) => {
|
||||
projectModel.create(projectData, (err, lastID) => (err ? reject(err) : resolve(lastID)));
|
||||
});
|
||||
res.json({ success: true, project_id: id });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 전체 조회
|
||||
exports.getAllProjects = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
projectModel.getAll((err, data) => (err ? reject(err) : resolve(data)));
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 단일 조회
|
||||
exports.getProjectById = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.project_id, 10);
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
projectModel.getById(id, (err, data) => (err ? reject(err) : resolve(data)));
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'Project not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 수정
|
||||
exports.updateProject = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.project_id, 10);
|
||||
const data = { ...req.body, project_id: id };
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
projectModel.update(data, (err, ch) => (err ? reject(err) : resolve(ch)));
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Project not found or no changes' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 삭제
|
||||
exports.removeProject = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.project_id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
projectModel.remove(id, (err, ch) => (err ? reject(err) : resolve(ch)));
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Project not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
69
api.hyungi.net/controllers/taskController.js
Normal file
69
api.hyungi.net/controllers/taskController.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const taskModel = require('../models/taskModel');
|
||||
|
||||
// 1. 생성
|
||||
exports.createTask = async (req, res) => {
|
||||
try {
|
||||
const taskData = req.body;
|
||||
const lastID = await new Promise((resolve, reject) => {
|
||||
taskModel.create(taskData, (err, id) => (err ? reject(err) : resolve(id)));
|
||||
});
|
||||
res.json({ success: true, task_id: lastID });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 전체 조회
|
||||
exports.getAllTasks = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
taskModel.getAll((err, data) => (err ? reject(err) : resolve(data)));
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 단일 조회
|
||||
exports.getTaskById = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.task_id, 10);
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
taskModel.getById(id, (err, data) => (err ? reject(err) : resolve(data)));
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'Task not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 수정
|
||||
exports.updateTask = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.task_id, 10);
|
||||
const taskData = { ...req.body, task_id: id };
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
taskModel.update(taskData, (err, ch) => (err ? reject(err) : resolve(ch)));
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Task not found or no change' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 삭제
|
||||
exports.removeTask = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.task_id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
taskModel.remove(id, (err, ch) => (err ? reject(err) : resolve(ch)));
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Task not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
76
api.hyungi.net/controllers/toolsController.js
Normal file
76
api.hyungi.net/controllers/toolsController.js
Normal file
@@ -0,0 +1,76 @@
|
||||
const Tools = require('../models/toolsModel');
|
||||
|
||||
// 1. 전체 도구 조회
|
||||
exports.getAll = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
Tools.getAllTools((err, data) => err ? reject(err) : resolve(data));
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 단일 도구 조회
|
||||
exports.getById = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
Tools.getToolById(id, (err, data) => err ? reject(err) : resolve(data));
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'Tool not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 도구 생성
|
||||
exports.create = async (req, res) => {
|
||||
try {
|
||||
const insertId = await new Promise((resolve, reject) => {
|
||||
Tools.createTool(req.body, (err, resultId) => {
|
||||
if (err) return reject(err);
|
||||
resolve(resultId);
|
||||
});
|
||||
});
|
||||
res.status(201).json({ success: true, id: insertId });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 도구 수정
|
||||
exports.update = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
const changedRows = await new Promise((resolve, reject) => {
|
||||
Tools.updateTool(id, req.body, (err, affectedRows) => {
|
||||
if (err) return reject(err);
|
||||
resolve(affectedRows);
|
||||
});
|
||||
});
|
||||
if (changedRows === 0) return res.status(404).json({ error: 'Tool not found or no change' });
|
||||
res.json({ success: true, changes: changedRows });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 도구 삭제
|
||||
exports.delete = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id, 10);
|
||||
const deletedRows = await new Promise((resolve, reject) => {
|
||||
Tools.deleteTool(id, (err, affectedRows) => {
|
||||
if (err) return reject(err);
|
||||
resolve(affectedRows);
|
||||
});
|
||||
});
|
||||
if (deletedRows === 0) return res.status(404).json({ error: 'Tool not found' });
|
||||
res.status(204).send();
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
26
api.hyungi.net/controllers/uploadController.js
Normal file
26
api.hyungi.net/controllers/uploadController.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const uploadModel = require('../models/uploadModel');
|
||||
|
||||
// 1. 문서 업로드
|
||||
exports.createUpload = async (req, res) => {
|
||||
try {
|
||||
const doc = req.body;
|
||||
const id = await new Promise((resolve, reject) => {
|
||||
uploadModel.create(doc, (err, insertId) => (err ? reject(err) : resolve(insertId)));
|
||||
});
|
||||
res.status(201).json({ success: true, id });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 전체 업로드 문서 조회
|
||||
exports.getUploads = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
uploadModel.getAll((err, data) => (err ? reject(err) : resolve(data)));
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
372
api.hyungi.net/controllers/workAnalysisController.js
Normal file
372
api.hyungi.net/controllers/workAnalysisController.js
Normal file
@@ -0,0 +1,372 @@
|
||||
// controllers/workAnalysisController.js
|
||||
|
||||
const WorkAnalysis = require('../models/WorkAnalysis');
|
||||
const { getDb } = require('../dbPool'); // 기존 프로젝트의 DB 연결 방식 사용
|
||||
|
||||
class WorkAnalysisController {
|
||||
constructor() {
|
||||
// 메서드 바인딩
|
||||
this.getStats = this.getStats.bind(this);
|
||||
this.getDailyTrend = this.getDailyTrend.bind(this);
|
||||
this.getWorkerStats = this.getWorkerStats.bind(this);
|
||||
this.getProjectStats = this.getProjectStats.bind(this);
|
||||
this.getWorkTypeStats = this.getWorkTypeStats.bind(this);
|
||||
this.getRecentWork = this.getRecentWork.bind(this);
|
||||
this.getWeekdayPattern = this.getWeekdayPattern.bind(this);
|
||||
this.getErrorAnalysis = this.getErrorAnalysis.bind(this);
|
||||
this.getMonthlyComparison = this.getMonthlyComparison.bind(this);
|
||||
this.getWorkerSpecialization = this.getWorkerSpecialization.bind(this);
|
||||
}
|
||||
|
||||
// 날짜 유효성 검사
|
||||
validateDateRange(startDate, endDate) {
|
||||
if (!startDate || !endDate) {
|
||||
throw new Error('시작일과 종료일을 입력해주세요.');
|
||||
}
|
||||
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
|
||||
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
|
||||
throw new Error('올바른 날짜 형식을 입력해주세요. (YYYY-MM-DD)');
|
||||
}
|
||||
|
||||
if (start > end) {
|
||||
throw new Error('시작일이 종료일보다 늦을 수 없습니다.');
|
||||
}
|
||||
|
||||
// 너무 긴 기간 방지 (1년 제한)
|
||||
const diffTime = Math.abs(end - start);
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
if (diffDays > 365) {
|
||||
throw new Error('조회 기간은 1년을 초과할 수 없습니다.');
|
||||
}
|
||||
|
||||
return { start, end };
|
||||
}
|
||||
|
||||
// 기본 통계 조회
|
||||
async getStats(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const stats = await workAnalysis.getBasicStats(start, end);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: stats,
|
||||
message: '기본 통계 조회 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('기본 통계 조회 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 일별 작업시간 추이
|
||||
async getDailyTrend(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const trendData = await workAnalysis.getDailyTrend(start, end);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: trendData,
|
||||
message: '일별 추이 조회 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('일별 추이 조회 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 작업자별 통계
|
||||
async getWorkerStats(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const workerStats = await workAnalysis.getWorkerStats(start, end);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: workerStats,
|
||||
message: '작업자별 통계 조회 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('작업자별 통계 조회 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 프로젝트별 통계
|
||||
async getProjectStats(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const projectStats = await workAnalysis.getProjectStats(start, end);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: projectStats,
|
||||
message: '프로젝트별 통계 조회 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('프로젝트별 통계 조회 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 작업유형별 통계
|
||||
async getWorkTypeStats(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const workTypeStats = await workAnalysis.getWorkTypeStats(start, end);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: workTypeStats,
|
||||
message: '작업유형별 통계 조회 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('작업유형별 통계 조회 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 최근 작업 현황
|
||||
async getRecentWork(req, res) {
|
||||
try {
|
||||
const { start, end, limit = 10 } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
// limit 유효성 검사
|
||||
const limitNum = parseInt(limit);
|
||||
if (isNaN(limitNum) || limitNum < 1 || limitNum > 100) {
|
||||
throw new Error('limit은 1~100 사이의 숫자여야 합니다.');
|
||||
}
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const recentWork = await workAnalysis.getRecentWork(start, end, limitNum);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: recentWork,
|
||||
message: '최근 작업 현황 조회 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('최근 작업 현황 조회 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 요일별 패턴 분석
|
||||
async getWeekdayPattern(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const weekdayPattern = await workAnalysis.getWeekdayPattern(start, end);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: weekdayPattern,
|
||||
message: '요일별 패턴 분석 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('요일별 패턴 분석 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 에러 분석
|
||||
async getErrorAnalysis(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const errorAnalysis = await workAnalysis.getErrorAnalysis(start, end);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: errorAnalysis,
|
||||
message: '에러 분석 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('에러 분석 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 월별 비교 분석
|
||||
async getMonthlyComparison(req, res) {
|
||||
try {
|
||||
const { year = new Date().getFullYear() } = req.query;
|
||||
|
||||
const yearNum = parseInt(year);
|
||||
if (isNaN(yearNum) || yearNum < 2000 || yearNum > 2050) {
|
||||
throw new Error('올바른 연도를 입력해주세요. (2000-2050)');
|
||||
}
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const monthlyData = await workAnalysis.getMonthlyComparison(yearNum);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: monthlyData,
|
||||
message: '월별 비교 분석 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('월별 비교 분석 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 작업자별 전문분야 분석
|
||||
async getWorkerSpecialization(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
const specializationData = await workAnalysis.getWorkerSpecialization(start, end);
|
||||
|
||||
// 작업자별로 그룹화하여 정리
|
||||
const groupedData = specializationData.reduce((acc, item) => {
|
||||
if (!acc[item.worker_id]) {
|
||||
acc[item.worker_id] = [];
|
||||
}
|
||||
acc[item.worker_id].push({
|
||||
work_type_id: item.work_type_id,
|
||||
project_id: item.project_id,
|
||||
totalHours: item.totalHours,
|
||||
totalReports: item.totalReports,
|
||||
percentage: item.percentage
|
||||
});
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: groupedData,
|
||||
message: '작업자별 전문분야 분석 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('작업자별 전문분야 분석 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 대시보드용 종합 데이터
|
||||
async getDashboardData(req, res) {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
this.validateDateRange(start, end);
|
||||
|
||||
const db = await getDb();
|
||||
const workAnalysis = new WorkAnalysis(db);
|
||||
|
||||
// 병렬로 여러 데이터 조회
|
||||
const [
|
||||
stats,
|
||||
dailyTrend,
|
||||
workerStats,
|
||||
projectStats,
|
||||
workTypeStats,
|
||||
recentWork
|
||||
] = await Promise.all([
|
||||
workAnalysis.getBasicStats(start, end),
|
||||
workAnalysis.getDailyTrend(start, end),
|
||||
workAnalysis.getWorkerStats(start, end),
|
||||
workAnalysis.getProjectStats(start, end),
|
||||
workAnalysis.getWorkTypeStats(start, end),
|
||||
workAnalysis.getRecentWork(start, end, 10)
|
||||
]);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
stats,
|
||||
dailyTrend,
|
||||
workerStats,
|
||||
projectStats,
|
||||
workTypeStats,
|
||||
recentWork
|
||||
},
|
||||
message: '대시보드 데이터 조회 완료'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('대시보드 데이터 조회 오류:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new WorkAnalysisController();
|
||||
134
api.hyungi.net/controllers/workReportController.js
Normal file
134
api.hyungi.net/controllers/workReportController.js
Normal file
@@ -0,0 +1,134 @@
|
||||
// controllers/workReportController.js
|
||||
const workReportModel = require('../models/workReportModel');
|
||||
|
||||
// 1. CREATE: 단일 또는 다중 보고서 등록
|
||||
exports.createWorkReport = async (req, res) => {
|
||||
try {
|
||||
const reports = Array.isArray(req.body) ? req.body : [req.body];
|
||||
const workReport_ids = [];
|
||||
|
||||
for (const report of reports) {
|
||||
const id = await new Promise((resolve, reject) => {
|
||||
workReportModel.create(report, (err, insertId) => {
|
||||
if (err) reject(err);
|
||||
else resolve(insertId);
|
||||
});
|
||||
});
|
||||
workReport_ids.push(id);
|
||||
}
|
||||
|
||||
res.json({ success: true, workReport_ids });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. READ BY DATE
|
||||
exports.getWorkReportsByDate = async (req, res) => {
|
||||
try {
|
||||
const { date } = req.params;
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
workReportModel.getAllByDate(date, (err, data) => {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. READ BY RANGE
|
||||
exports.getWorkReportsInRange = async (req, res) => {
|
||||
try {
|
||||
const { start, end } = req.query;
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
workReportModel.getByRange(start, end, (err, data) => {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. READ ONE
|
||||
exports.getWorkReportById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
workReportModel.getById(id, (err, data) => {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'WorkReport not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. UPDATE
|
||||
exports.updateWorkReport = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
workReportModel.update(id, req.body, (err, affectedRows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(affectedRows);
|
||||
});
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'No changes or not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 6. DELETE
|
||||
exports.removeWorkReport = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
workReportModel.remove(id, (err, affectedRows) => {
|
||||
if (err) reject(err);
|
||||
else resolve(affectedRows);
|
||||
});
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'WorkReport not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 7. SUMMARY (월간)
|
||||
exports.getSummary = async (req, res) => {
|
||||
try {
|
||||
const { year, month } = req.query;
|
||||
if (!year || !month) {
|
||||
return res.status(400).json({ error: '연도와 월이 필요합니다 (year, month)' });
|
||||
}
|
||||
|
||||
const start = `${year.padStart(4, '0')}-${month.padStart(2, '0')}-01`;
|
||||
const end = `${year.padStart(4, '0')}-${month.padStart(2, '0')}-31`;
|
||||
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
workReportModel.getByRange(start, end, (err, data) => {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
if (!rows || rows.length === 0) {
|
||||
return res.status(404).json({ error: 'WorkReport not found' });
|
||||
}
|
||||
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
85
api.hyungi.net/controllers/workerController.js
Normal file
85
api.hyungi.net/controllers/workerController.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// controllers/workerController.js
|
||||
const workerModel = require('../models/workerModel');
|
||||
|
||||
// 1. 작업자 생성
|
||||
exports.createWorker = async (req, res) => {
|
||||
try {
|
||||
const workerData = req.body;
|
||||
const lastID = await new Promise((resolve, reject) => {
|
||||
workerModel.create(workerData, (err, id) => {
|
||||
if (err) reject(err);
|
||||
else resolve(id);
|
||||
});
|
||||
});
|
||||
res.json({ success: true, worker_id: lastID });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 전체 작업자 조회
|
||||
exports.getAllWorkers = async (req, res) => {
|
||||
try {
|
||||
const rows = await new Promise((resolve, reject) => {
|
||||
workerModel.getAll((err, data) => {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 3. 단일 작업자 조회
|
||||
exports.getWorkerById = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.worker_id, 10);
|
||||
const row = await new Promise((resolve, reject) => {
|
||||
workerModel.getById(id, (err, data) => {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
if (!row) return res.status(404).json({ error: 'Worker not found' });
|
||||
res.json(row);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 4. 작업자 수정
|
||||
exports.updateWorker = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.worker_id, 10);
|
||||
const workerData = { ...req.body, worker_id: id };
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
workerModel.update(workerData, (err, affected) => {
|
||||
if (err) reject(err);
|
||||
else resolve(affected);
|
||||
});
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Worker not found or no change' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 작업자 삭제
|
||||
exports.removeWorker = async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.worker_id, 10);
|
||||
const changes = await new Promise((resolve, reject) => {
|
||||
workerModel.remove(id, (err, affected) => {
|
||||
if (err) reject(err);
|
||||
else resolve(affected);
|
||||
});
|
||||
});
|
||||
if (changes === 0) return res.status(404).json({ error: 'Worker not found' });
|
||||
res.json({ success: true, changes });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message || String(err) });
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user