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:
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* worker_id → user_id 통합 마이그레이션 (Phase 1)
|
||||
*
|
||||
* 비파괴적 추가: 기존 worker_id 컬럼은 유지하면서 user_id 컬럼을 추가하고 백필.
|
||||
* 코드 변경 전이므로 기존 시스템 동작에 영향 없음.
|
||||
*
|
||||
* 대상 테이블:
|
||||
* 1. departments - is_production 플래그 추가
|
||||
* 2. sso_users - department_id 추가 (없는 경우)
|
||||
* 3. workers - user_id 추가 + 매핑 백필
|
||||
* 4~15. 12개 참조 테이블 - user_id 추가 + 백필
|
||||
*
|
||||
* @since 2026-03-05
|
||||
*/
|
||||
|
||||
exports.up = async function(knex) {
|
||||
// ============================================================
|
||||
// 1. departments 테이블에 is_production 플래그 추가
|
||||
// ============================================================
|
||||
const hasIsProduction = await knex.schema.hasColumn('departments', 'is_production');
|
||||
if (!hasIsProduction) {
|
||||
await knex.schema.table('departments', (table) => {
|
||||
table.boolean('is_production').defaultTo(false).comment('생산직 부서 여부');
|
||||
});
|
||||
await knex.raw(`UPDATE departments SET is_production = TRUE WHERE department_name LIKE '%생산%'`);
|
||||
console.log('✅ departments.is_production 추가 완료');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 2. sso_users에 department_id 추가 (없는 경우)
|
||||
// ============================================================
|
||||
const hasSsoDeptId = await knex.schema.hasColumn('sso_users', 'department_id');
|
||||
if (!hasSsoDeptId) {
|
||||
await knex.schema.table('sso_users', (table) => {
|
||||
table.integer('department_id').unsigned().defaultTo(null).comment('소속 부서 ID');
|
||||
});
|
||||
// 기존 department(문자열) → department_id(FK) 매핑
|
||||
await knex.raw(`
|
||||
UPDATE sso_users s
|
||||
INNER JOIN departments d ON s.department = d.department_name
|
||||
SET s.department_id = d.department_id
|
||||
WHERE s.department IS NOT NULL
|
||||
`);
|
||||
console.log('✅ sso_users.department_id 추가 및 백필 완료');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 3. workers 테이블에 user_id 추가 + 매핑 백필
|
||||
// ============================================================
|
||||
const hasWorkersUserId = await knex.schema.hasColumn('workers', 'user_id');
|
||||
if (!hasWorkersUserId) {
|
||||
await knex.schema.table('workers', (table) => {
|
||||
table.integer('user_id').unsigned().defaultTo(null).after('worker_id')
|
||||
.comment('sso_users.user_id 매핑');
|
||||
});
|
||||
// users 테이블을 경유하여 sso_users.user_id 매핑
|
||||
await knex.raw(`
|
||||
UPDATE workers w
|
||||
INNER JOIN users u ON u.worker_id = w.worker_id
|
||||
INNER JOIN sso_users s ON s.username = u.username
|
||||
SET w.user_id = s.user_id
|
||||
`);
|
||||
// user_id에 인덱스 추가
|
||||
await knex.raw(`ALTER TABLE workers ADD INDEX idx_workers_user_id (user_id)`);
|
||||
console.log('✅ workers.user_id 추가 및 백필 완료');
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 4~15. 12개 참조 테이블에 user_id 컬럼 추가 + 백필
|
||||
// ============================================================
|
||||
|
||||
// worker_id 컬럼을 가진 테이블들
|
||||
const tablesWithWorkerId = [
|
||||
'tbm_team_assignments',
|
||||
'tbm_transfers',
|
||||
'daily_work_reports',
|
||||
'daily_attendance_records',
|
||||
'worker_vacation_balance',
|
||||
'vacation_requests',
|
||||
'vacation_balance_details',
|
||||
'worker_groups',
|
||||
'monthly_worker_status',
|
||||
];
|
||||
|
||||
for (const tableName of tablesWithWorkerId) {
|
||||
const tableExists = await knex.schema.hasTable(tableName);
|
||||
if (!tableExists) {
|
||||
console.log(`⏭️ ${tableName} 테이블이 존재하지 않음, 건너뜀`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const hasUserId = await knex.schema.hasColumn(tableName, 'user_id');
|
||||
if (!hasUserId) {
|
||||
await knex.schema.table(tableName, (table) => {
|
||||
table.integer('user_id').unsigned().defaultTo(null).comment('sso_users.user_id');
|
||||
});
|
||||
// 백필: workers 테이블의 user_id 매핑 사용
|
||||
await knex.raw(`
|
||||
UPDATE ${tableName} t
|
||||
INNER JOIN workers w ON t.worker_id = w.worker_id
|
||||
SET t.user_id = w.user_id
|
||||
WHERE w.user_id IS NOT NULL
|
||||
`);
|
||||
// 인덱스 추가
|
||||
await knex.raw(`ALTER TABLE ${tableName} ADD INDEX idx_${tableName}_user_id (user_id)`);
|
||||
console.log(`✅ ${tableName}.user_id 추가 및 백필 완료`);
|
||||
}
|
||||
}
|
||||
|
||||
// DailyIssueReports (대소문자 다른 테이블명)
|
||||
const hasDIR = await knex.schema.hasTable('DailyIssueReports');
|
||||
if (hasDIR) {
|
||||
const hasDIRUserId = await knex.schema.hasColumn('DailyIssueReports', 'user_id');
|
||||
if (!hasDIRUserId) {
|
||||
await knex.schema.table('DailyIssueReports', (table) => {
|
||||
table.integer('user_id').unsigned().defaultTo(null).comment('sso_users.user_id');
|
||||
});
|
||||
await knex.raw(`
|
||||
UPDATE DailyIssueReports t
|
||||
INNER JOIN workers w ON t.worker_id = w.worker_id
|
||||
SET t.user_id = w.user_id
|
||||
WHERE w.user_id IS NOT NULL
|
||||
`);
|
||||
console.log('✅ DailyIssueReports.user_id 추가 및 백필 완료');
|
||||
}
|
||||
}
|
||||
|
||||
// WorkReports (대소문자 다른 테이블명)
|
||||
const hasWR = await knex.schema.hasTable('WorkReports');
|
||||
if (hasWR) {
|
||||
const hasWRUserId = await knex.schema.hasColumn('WorkReports', 'user_id');
|
||||
if (!hasWRUserId) {
|
||||
await knex.schema.table('WorkReports', (table) => {
|
||||
table.integer('user_id').unsigned().defaultTo(null).comment('sso_users.user_id');
|
||||
});
|
||||
await knex.raw(`
|
||||
UPDATE WorkReports t
|
||||
INNER JOIN workers w ON t.worker_id = w.worker_id
|
||||
SET t.user_id = w.user_id
|
||||
WHERE w.user_id IS NOT NULL
|
||||
`);
|
||||
console.log('✅ WorkReports.user_id 추가 및 백필 완료');
|
||||
}
|
||||
}
|
||||
|
||||
// tbm_sessions: leader_id → leader_user_id 추가
|
||||
const hasLeaderUserId = await knex.schema.hasColumn('tbm_sessions', 'leader_user_id');
|
||||
if (!hasLeaderUserId) {
|
||||
await knex.schema.table('tbm_sessions', (table) => {
|
||||
table.integer('leader_user_id').unsigned().defaultTo(null).comment('조장 sso_users.user_id');
|
||||
});
|
||||
await knex.raw(`
|
||||
UPDATE tbm_sessions t
|
||||
INNER JOIN workers w ON t.leader_id = w.worker_id
|
||||
SET t.leader_user_id = w.user_id
|
||||
WHERE w.user_id IS NOT NULL
|
||||
`);
|
||||
await knex.raw(`ALTER TABLE tbm_sessions ADD INDEX idx_tbm_sessions_leader_user_id (leader_user_id)`);
|
||||
console.log('✅ tbm_sessions.leader_user_id 추가 및 백필 완료');
|
||||
}
|
||||
|
||||
// team_handovers: from/to_leader_id → from/to_leader_user_id 추가
|
||||
const hasFromLeaderUserId = await knex.schema.hasColumn('team_handovers', 'from_leader_user_id');
|
||||
if (!hasFromLeaderUserId) {
|
||||
await knex.schema.table('team_handovers', (table) => {
|
||||
table.integer('from_leader_user_id').unsigned().defaultTo(null).comment('인계자 sso_users.user_id');
|
||||
table.integer('to_leader_user_id').unsigned().defaultTo(null).comment('인수자 sso_users.user_id');
|
||||
});
|
||||
await knex.raw(`
|
||||
UPDATE team_handovers t
|
||||
INNER JOIN workers w1 ON t.from_leader_id = w1.worker_id
|
||||
SET t.from_leader_user_id = w1.user_id
|
||||
WHERE w1.user_id IS NOT NULL
|
||||
`);
|
||||
await knex.raw(`
|
||||
UPDATE team_handovers t
|
||||
INNER JOIN workers w2 ON t.to_leader_id = w2.worker_id
|
||||
SET t.to_leader_user_id = w2.user_id
|
||||
WHERE w2.user_id IS NOT NULL
|
||||
`);
|
||||
console.log('✅ team_handovers.from/to_leader_user_id 추가 및 백필 완료');
|
||||
}
|
||||
|
||||
console.log('🎉 Phase 1 마이그레이션 완료: 모든 테이블에 user_id 컬럼 추가 및 백필 완료');
|
||||
};
|
||||
|
||||
exports.down = async function(knex) {
|
||||
// user_id 컬럼 제거 (롤백)
|
||||
const columnsToRemove = [
|
||||
['departments', 'is_production'],
|
||||
['workers', 'user_id'],
|
||||
['tbm_team_assignments', 'user_id'],
|
||||
['tbm_transfers', 'user_id'],
|
||||
['daily_work_reports', 'user_id'],
|
||||
['daily_attendance_records', 'user_id'],
|
||||
['worker_vacation_balance', 'user_id'],
|
||||
['vacation_requests', 'user_id'],
|
||||
['vacation_balance_details', 'user_id'],
|
||||
['worker_groups', 'user_id'],
|
||||
['monthly_worker_status', 'user_id'],
|
||||
['tbm_sessions', 'leader_user_id'],
|
||||
['team_handovers', 'from_leader_user_id'],
|
||||
['team_handovers', 'to_leader_user_id'],
|
||||
];
|
||||
|
||||
for (const [tableName, columnName] of columnsToRemove) {
|
||||
const tableExists = await knex.schema.hasTable(tableName);
|
||||
if (!tableExists) continue;
|
||||
const hasColumn = await knex.schema.hasColumn(tableName, columnName);
|
||||
if (hasColumn) {
|
||||
await knex.schema.table(tableName, (table) => {
|
||||
table.dropColumn(columnName);
|
||||
});
|
||||
console.log(`↩️ ${tableName}.${columnName} 제거`);
|
||||
}
|
||||
}
|
||||
|
||||
// 대소문자 다른 테이블
|
||||
for (const tableName of ['DailyIssueReports', 'WorkReports']) {
|
||||
const tableExists = await knex.schema.hasTable(tableName);
|
||||
if (tableExists) {
|
||||
const hasColumn = await knex.schema.hasColumn(tableName, 'user_id');
|
||||
if (hasColumn) {
|
||||
await knex.schema.table(tableName, (table) => {
|
||||
table.dropColumn('user_id');
|
||||
});
|
||||
console.log(`↩️ ${tableName}.user_id 제거`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sso_users.department_id는 유지 (다른 마이그레이션에서 관리)
|
||||
};
|
||||
Reference in New Issue
Block a user