- tkuser 서비스 신규 추가 (API + Web) - 사용자/권한/프로젝트/부서/작업자/작업장/설비/작업/휴가 통합 관리 - 작업장 탭: 공장→작업장 드릴다운 네비게이션 + 구역지도 클릭 연동 - 작업 탭: 공정(work_types)→작업(tasks) 계층 관리 - 휴가 탭: 유형 관리 + 연차 배정(근로기준법 자동계산) - 전 시스템 SSO 쿠키 인증으로 통합 (.technicalkorea.net 공유) - System 2: 작업 이슈 리포트 기능 강화 - System 3: tkuser API 연동, 페이지 권한 체계 적용 - docker-compose에 tkuser-api, tkuser-web 서비스 추가 - ARCHITECTURE.md, DEPLOYMENT.md 문서 작성 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
193 lines
8.0 KiB
JavaScript
193 lines
8.0 KiB
JavaScript
/**
|
|
* Equipment Model
|
|
*
|
|
* equipments + equipment_photos CRUD (MariaDB)
|
|
*/
|
|
|
|
const { getPool } = require('./userModel');
|
|
|
|
// ==================== 기본 CRUD ====================
|
|
|
|
async function getAll(filters = {}) {
|
|
const db = getPool();
|
|
let sql = `SELECT e.*, w.workplace_name, c.category_name
|
|
FROM equipments e
|
|
LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id
|
|
LEFT JOIN workplace_categories c ON w.category_id = c.category_id`;
|
|
const conditions = [];
|
|
const values = [];
|
|
|
|
if (filters.workplace_id) { conditions.push('e.workplace_id = ?'); values.push(filters.workplace_id); }
|
|
if (filters.equipment_type) { conditions.push('e.equipment_type = ?'); values.push(filters.equipment_type); }
|
|
if (filters.status) { conditions.push('e.status = ?'); values.push(filters.status); }
|
|
if (filters.search) {
|
|
conditions.push('(e.equipment_name LIKE ? OR e.equipment_code LIKE ?)');
|
|
const term = `%${filters.search}%`;
|
|
values.push(term, term);
|
|
}
|
|
|
|
if (conditions.length) sql += ' WHERE ' + conditions.join(' AND ');
|
|
sql += ' ORDER BY e.equipment_code ASC';
|
|
|
|
const [rows] = await db.query(sql, values);
|
|
return rows;
|
|
}
|
|
|
|
async function getById(id) {
|
|
const db = getPool();
|
|
const [rows] = await db.query(
|
|
`SELECT e.*, w.workplace_name, c.category_name
|
|
FROM equipments e
|
|
LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id
|
|
LEFT JOIN workplace_categories c ON w.category_id = c.category_id
|
|
WHERE e.equipment_id = ?`,
|
|
[id]
|
|
);
|
|
return rows[0] || null;
|
|
}
|
|
|
|
async function getByWorkplace(workplaceId) {
|
|
const db = getPool();
|
|
const [rows] = await db.query(
|
|
`SELECT e.*, w.workplace_name
|
|
FROM equipments e
|
|
LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id
|
|
WHERE e.workplace_id = ?
|
|
ORDER BY e.equipment_code ASC`,
|
|
[workplaceId]
|
|
);
|
|
return rows;
|
|
}
|
|
|
|
async function create(data) {
|
|
const db = getPool();
|
|
const [result] = await db.query(
|
|
`INSERT INTO equipments (equipment_code, equipment_name, equipment_type, model_name, manufacturer, supplier, purchase_price, installation_date, serial_number, specifications, status, notes, workplace_id, map_x_percent, map_y_percent, map_width_percent, map_height_percent)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
data.equipment_code, data.equipment_name,
|
|
data.equipment_type || null, data.model_name || null,
|
|
data.manufacturer || null, data.supplier || null,
|
|
data.purchase_price || null, data.installation_date || null,
|
|
data.serial_number || null, data.specifications || null,
|
|
data.status || 'active', data.notes || null,
|
|
data.workplace_id || null,
|
|
data.map_x_percent || null, data.map_y_percent || null,
|
|
data.map_width_percent || null, data.map_height_percent || null
|
|
]
|
|
);
|
|
return getById(result.insertId);
|
|
}
|
|
|
|
async function update(id, data) {
|
|
const db = getPool();
|
|
const fields = [];
|
|
const values = [];
|
|
|
|
if (data.equipment_code !== undefined) { fields.push('equipment_code = ?'); values.push(data.equipment_code); }
|
|
if (data.equipment_name !== undefined) { fields.push('equipment_name = ?'); values.push(data.equipment_name); }
|
|
if (data.equipment_type !== undefined) { fields.push('equipment_type = ?'); values.push(data.equipment_type || null); }
|
|
if (data.model_name !== undefined) { fields.push('model_name = ?'); values.push(data.model_name || null); }
|
|
if (data.manufacturer !== undefined) { fields.push('manufacturer = ?'); values.push(data.manufacturer || null); }
|
|
if (data.supplier !== undefined) { fields.push('supplier = ?'); values.push(data.supplier || null); }
|
|
if (data.purchase_price !== undefined) { fields.push('purchase_price = ?'); values.push(data.purchase_price || null); }
|
|
if (data.installation_date !== undefined) { fields.push('installation_date = ?'); values.push(data.installation_date || null); }
|
|
if (data.serial_number !== undefined) { fields.push('serial_number = ?'); values.push(data.serial_number || null); }
|
|
if (data.specifications !== undefined) { fields.push('specifications = ?'); values.push(data.specifications || null); }
|
|
if (data.status !== undefined) { fields.push('status = ?'); values.push(data.status); }
|
|
if (data.notes !== undefined) { fields.push('notes = ?'); values.push(data.notes || null); }
|
|
if (data.workplace_id !== undefined) { fields.push('workplace_id = ?'); values.push(data.workplace_id || null); }
|
|
if (data.map_x_percent !== undefined) { fields.push('map_x_percent = ?'); values.push(data.map_x_percent); }
|
|
if (data.map_y_percent !== undefined) { fields.push('map_y_percent = ?'); values.push(data.map_y_percent); }
|
|
if (data.map_width_percent !== undefined) { fields.push('map_width_percent = ?'); values.push(data.map_width_percent); }
|
|
if (data.map_height_percent !== undefined) { fields.push('map_height_percent = ?'); values.push(data.map_height_percent); }
|
|
|
|
if (fields.length === 0) return getById(id);
|
|
|
|
values.push(id);
|
|
await db.query(`UPDATE equipments SET ${fields.join(', ')} WHERE equipment_id = ?`, values);
|
|
return getById(id);
|
|
}
|
|
|
|
async function remove(id) {
|
|
const db = getPool();
|
|
await db.query('DELETE FROM equipments WHERE equipment_id = ?', [id]);
|
|
}
|
|
|
|
async function getEquipmentTypes() {
|
|
const db = getPool();
|
|
const [rows] = await db.query(
|
|
'SELECT DISTINCT equipment_type FROM equipments WHERE equipment_type IS NOT NULL AND equipment_type != "" ORDER BY equipment_type ASC'
|
|
);
|
|
return rows.map(r => r.equipment_type);
|
|
}
|
|
|
|
async function getNextCode(prefix = 'TKP') {
|
|
const db = getPool();
|
|
const [rows] = await db.query(
|
|
'SELECT equipment_code FROM equipments WHERE equipment_code LIKE ? ORDER BY equipment_code DESC LIMIT 1',
|
|
[`${prefix}-%`]
|
|
);
|
|
if (!rows.length) return `${prefix}-001`;
|
|
const lastNum = parseInt(rows[0].equipment_code.replace(`${prefix}-`, ''), 10) || 0;
|
|
return `${prefix}-${String(lastNum + 1).padStart(3, '0')}`;
|
|
}
|
|
|
|
async function checkDuplicateCode(code, excludeId) {
|
|
const db = getPool();
|
|
let sql = 'SELECT equipment_id FROM equipments WHERE equipment_code = ?';
|
|
const values = [code];
|
|
if (excludeId) { sql += ' AND equipment_id != ?'; values.push(excludeId); }
|
|
const [rows] = await db.query(sql, values);
|
|
return rows.length > 0;
|
|
}
|
|
|
|
// ==================== 지도 위치 ====================
|
|
|
|
async function updateMapPosition(id, positionData) {
|
|
const db = getPool();
|
|
const fields = ['map_x_percent = ?', 'map_y_percent = ?', 'map_width_percent = ?', 'map_height_percent = ?'];
|
|
const values = [positionData.map_x_percent, positionData.map_y_percent, positionData.map_width_percent, positionData.map_height_percent];
|
|
if (positionData.workplace_id !== undefined) {
|
|
fields.push('workplace_id = ?');
|
|
values.push(positionData.workplace_id);
|
|
}
|
|
values.push(id);
|
|
await db.query(`UPDATE equipments SET ${fields.join(', ')} WHERE equipment_id = ?`, values);
|
|
return getById(id);
|
|
}
|
|
|
|
// ==================== 사진 ====================
|
|
|
|
async function addPhoto(equipmentId, photoData) {
|
|
const db = getPool();
|
|
const [result] = await db.query(
|
|
`INSERT INTO equipment_photos (equipment_id, photo_path, description, display_order, uploaded_by) VALUES (?, ?, ?, ?, ?)`,
|
|
[equipmentId, photoData.photo_path, photoData.description || null, photoData.display_order || 0, photoData.uploaded_by || null]
|
|
);
|
|
return { photo_id: result.insertId, equipment_id: equipmentId, ...photoData };
|
|
}
|
|
|
|
async function getPhotos(equipmentId) {
|
|
const db = getPool();
|
|
const [rows] = await db.query(
|
|
'SELECT * FROM equipment_photos WHERE equipment_id = ? ORDER BY display_order ASC, created_at ASC',
|
|
[equipmentId]
|
|
);
|
|
return rows;
|
|
}
|
|
|
|
async function deletePhoto(photoId) {
|
|
const db = getPool();
|
|
const [photo] = await db.query('SELECT photo_path FROM equipment_photos WHERE photo_id = ?', [photoId]);
|
|
await db.query('DELETE FROM equipment_photos WHERE photo_id = ?', [photoId]);
|
|
return { photo_id: photoId, photo_path: photo[0]?.photo_path };
|
|
}
|
|
|
|
module.exports = {
|
|
getAll, getById, getByWorkplace, create, update, remove,
|
|
getEquipmentTypes, getNextCode, checkDuplicateCode,
|
|
updateMapPosition,
|
|
addPhoto, getPhotos, deletePhoto
|
|
};
|