refactor: worker_id → user_id 전체 마이그레이션 (Phase 1-4)

sso_users.user_id를 단일 식별자로 통합. JWT에서 worker_id 제거,
department_id/is_production 추가. 백엔드 15개 모델, 11개 컨트롤러,
4개 서비스, 7개 라우트, 프론트엔드 32+ JS/11+ HTML 변환.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-05 13:13:10 +09:00
parent 2197cdb3d5
commit abd7564e6b
90 changed files with 1790 additions and 925 deletions

View File

@@ -794,14 +794,14 @@ document.addEventListener('DOMContentLoaded', () => {
// ========== 작업자 연결 기능 ========== //
let departments = [];
let selectedWorkerId = null;
let selectedUserId = null;
// 연결된 작업자 정보 표시 업데이트
function updateLinkedWorkerDisplay(user) {
const linkedWorkerInfo = document.getElementById('linkedWorkerInfo');
if (!linkedWorkerInfo) return;
if (user.worker_id && user.worker_name) {
if (user.user_id && user.worker_name) {
linkedWorkerInfo.innerHTML = `
<span class="worker-badge">
<span class="worker-name">👤 ${user.worker_name}</span>
@@ -820,7 +820,7 @@ async function openWorkerSelectModal() {
return;
}
selectedWorkerId = currentEditingUser.worker_id || null;
selectedUserId = currentEditingUser.user_id || null;
// 부서 목록 로드
await loadDepartmentsForSelect();
@@ -833,7 +833,7 @@ window.openWorkerSelectModal = openWorkerSelectModal;
// 작업자 선택 모달 닫기
function closeWorkerSelectModal() {
document.getElementById('workerSelectModal').style.display = 'none';
selectedWorkerId = null;
selectedUserId = null;
}
window.closeWorkerSelectModal = closeWorkerSelectModal;
@@ -906,18 +906,18 @@ function renderWorkerListForSelect(workers) {
}
// 이미 다른 계정에 연결된 작업자 확인을 위해 users 배열 사용
const linkedWorkerIds = users
.filter(u => u.worker_id && u.user_id !== currentEditingUser?.user_id)
.map(u => u.worker_id);
const linkedUserIds = users
.filter(u => u.user_id && u.user_id !== currentEditingUser?.user_id)
.map(u => u.user_id);
container.innerHTML = workers.map(worker => {
const isSelected = selectedWorkerId === worker.worker_id;
const isLinkedToOther = linkedWorkerIds.includes(worker.worker_id);
const linkedUser = isLinkedToOther ? users.find(u => u.worker_id === worker.worker_id) : null;
const isSelected = selectedUserId === worker.user_id;
const isLinkedToOther = linkedUserIds.includes(worker.user_id);
const linkedUser = isLinkedToOther ? users.find(u => u.user_id === worker.user_id) : null;
return `
<div class="worker-select-item ${isSelected ? 'selected' : ''} ${isLinkedToOther ? 'disabled' : ''}"
onclick="${isLinkedToOther ? '' : `selectWorker(${worker.worker_id}, '${worker.worker_name}')`}">
onclick="${isLinkedToOther ? '' : `selectWorker(${worker.user_id}, '${worker.worker_name}')`}">
<div class="worker-avatar">${worker.worker_name.charAt(0)}</div>
<div class="worker-info">
<div class="worker-name">${worker.worker_name}</div>
@@ -941,8 +941,8 @@ function getJobTypeName(jobType) {
}
// 작업자 선택
async function selectWorker(workerId, workerName) {
selectedWorkerId = workerId;
async function selectWorker(userId, workerName) {
selectedUserId = userId;
// UI 업데이트
document.querySelectorAll('.worker-select-item').forEach(item => {
@@ -950,7 +950,7 @@ async function selectWorker(workerId, workerName) {
item.querySelector('.select-indicator').textContent = '';
});
const selectedItem = document.querySelector(`.worker-select-item[onclick*="${workerId}"]`);
const selectedItem = document.querySelector(`.worker-select-item[onclick*="${userId}"]`);
if (selectedItem) {
selectedItem.classList.add('selected');
selectedItem.querySelector('.select-indicator').textContent = '✓';
@@ -959,12 +959,12 @@ async function selectWorker(workerId, workerName) {
// 서버에 저장
try {
const response = await window.apiCall(`/users/${currentEditingUser.user_id}`, 'PUT', {
worker_id: workerId
user_id: userId
});
if (response.success) {
// currentEditingUser 업데이트
currentEditingUser.worker_id = workerId;
currentEditingUser.user_id = userId;
currentEditingUser.worker_name = workerName;
// 부서 정보도 업데이트
@@ -1003,7 +1003,7 @@ async function unlinkWorker() {
return;
}
if (!currentEditingUser.worker_id) {
if (!currentEditingUser.user_id) {
showToast('연결된 작업자가 없습니다.', 'warning');
closeWorkerSelectModal();
return;
@@ -1015,19 +1015,19 @@ async function unlinkWorker() {
try {
const response = await window.apiCall(`/users/${currentEditingUser.user_id}`, 'PUT', {
worker_id: null
user_id: null
});
if (response.success) {
// currentEditingUser 업데이트
currentEditingUser.worker_id = null;
currentEditingUser.user_id = null;
currentEditingUser.worker_name = null;
currentEditingUser.department_name = null;
// users 배열 업데이트
const userIndex = users.findIndex(u => u.user_id === currentEditingUser.user_id);
if (userIndex !== -1) {
users[userIndex] = { ...users[userIndex], worker_id: null, worker_name: null, department_name: null };
users[userIndex] = { ...users[userIndex], user_id: null, worker_name: null, department_name: null };
}
// 표시 업데이트