refactor: worker_id 잔재 제거 - user_id 기반으로 완전 전환

- workerModel: remove()를 user_id 기반 cascading delete로 전환
- workerController: 계정 생성/해제를 workers.user_id 연결 방식으로 변경
- userController: JOIN 방향 전환 (u.worker_id→w.worker_id 에서 w.user_id→u.user_id)
- authController, systemController, authRoutes: 모든 CRUD에서 worker_id 참조 제거
- DB: UNIQUE KEY 5개 교체, FK 7개 삭제, daily_worker_summary user_id 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-06 07:47:01 +09:00
parent 7089548722
commit d385ce7ac1
6 changed files with 110 additions and 150 deletions

View File

@@ -161,11 +161,10 @@ router.post('/refresh-token', async (req, res) => {
// 새 토큰 발급
const newToken = jwt.sign(
{
{
user_id: user.user_id,
username: user.username,
access_level: user.access_level,
worker_id: user.worker_id,
name: user.name || user.username
},
process.env.JWT_SECRET || 'your-secret-key',
@@ -456,10 +455,10 @@ router.get('/me', verifyToken, async (req, res) => {
connection = await mysql.createConnection(dbConfig);
const [rows] = await connection.execute(
'SELECT user_id, username, name, email, access_level, worker_id, last_login_at, created_at FROM users WHERE user_id = ?',
'SELECT user_id, username, name, email, access_level, last_login_at, created_at FROM users WHERE user_id = ?',
[userId]
);
if (rows.length === 0) {
return res.status(404).json({ error: '사용자를 찾을 수 없습니다.' });
}
@@ -471,7 +470,6 @@ router.get('/me', verifyToken, async (req, res) => {
name: user.name || user.username,
email: user.email,
access_level: user.access_level,
worker_id: user.worker_id,
last_login_at: user.last_login_at,
created_at: user.created_at
});
@@ -493,43 +491,43 @@ router.post('/register', verifyToken, async (req, res) => {
let connection;
try {
const { username, password, name, email, access_level, worker_id } = req.body;
const { username, password, name, email, access_level } = req.body;
// 권한 확인 (admin 이상만 사용자 등록 가능)
if (!['admin', 'system'].includes(req.user.access_level)) {
return res.status(403).json({
return res.status(403).json({
success: false,
error: '사용자 등록 권한이 없습니다.'
error: '사용자 등록 권한이 없습니다.'
});
}
if (!username || !password || !name || !access_level) {
return res.status(400).json({
return res.status(400).json({
success: false,
error: '필수 항목을 모두 입력해주세요.'
error: '필수 항목을 모두 입력해주세요.'
});
}
// 비밀번호 강도 검증
if (password.length < 6) {
return res.status(400).json({
return res.status(400).json({
success: false,
error: '비밀번호는 최소 6자 이상이어야 합니다.'
error: '비밀번호는 최소 6자 이상이어야 합니다.'
});
}
connection = await mysql.createConnection(dbConfig);
// 사용자명 중복 체크
const [existing] = await connection.execute(
'SELECT user_id FROM users WHERE username = ?',
[username]
);
if (existing.length > 0) {
return res.status(409).json({
return res.status(409).json({
success: false,
error: '이미 존재하는 사용자명입니다.'
error: '이미 존재하는 사용자명입니다.'
});
}
@@ -539,11 +537,11 @@ router.post('/register', verifyToken, async (req, res) => {
'SELECT user_id FROM users WHERE email = ?',
[email]
);
if (existingEmail.length > 0) {
return res.status(409).json({
return res.status(409).json({
success: false,
error: '이미 사용 중인 이메일입니다.'
error: '이미 사용 중인 이메일입니다.'
});
}
}
@@ -553,9 +551,9 @@ router.post('/register', verifyToken, async (req, res) => {
// 사용자 추가
const [result] = await connection.execute(
`INSERT INTO Users (username, password, name, email, access_level, worker_id, is_active, created_at, password_changed_at)
VALUES (?, ?, ?, ?, ?, ?, TRUE, NOW(), NOW())`,
[username, hashedPassword, name, email || null, access_level, worker_id || null]
`INSERT INTO Users (username, password, name, email, access_level, is_active, created_at, password_changed_at)
VALUES (?, ?, ?, ?, ?, TRUE, NOW(), NOW())`,
[username, hashedPassword, name, email || null, access_level]
);
// 비밀번호 변경 로그 기록 (초기 설정)
@@ -578,8 +576,7 @@ router.post('/register', verifyToken, async (req, res) => {
username,
name,
email: email || null,
access_level,
worker_id: worker_id || null
access_level
}
});
@@ -615,7 +612,6 @@ router.get('/users', verifyToken, async (req, res) => {
u.role_id,
r.name as role_name,
u._access_level_old as access_level,
u.worker_id,
u.is_active,
u.last_login_at,
u.created_at
@@ -649,8 +645,7 @@ router.get('/users', verifyToken, async (req, res) => {
email: user.email,
role_id: user.role_id,
role_name: user.role_name,
access_level: user.access_level || user.role_name?.toLowerCase(), // 하위 호환성
worker_id: user.worker_id,
access_level: user.access_level || user.role_name?.toLowerCase(),
is_active: user.is_active,
last_login_at: user.last_login_at,
created_at: user.created_at
@@ -676,22 +671,22 @@ router.put('/users/:id', verifyToken, async (req, res) => {
try {
const userId = parseInt(req.params.id);
const { name, email, access_level, worker_id, password, is_active } = req.body;
const { name, email, access_level, password, is_active } = req.body;
// 권한 확인
if (!['admin', 'system'].includes(req.user.access_level)) {
// 일반 사용자는 자신의 정보만 수정 가능 (이름, 이메일만)
if (userId !== req.user.user_id) {
return res.status(403).json({
return res.status(403).json({
success: false,
error: '다른 사용자의 정보를 수정할 권한이 없습니다.'
error: '다른 사용자의 정보를 수정할 권한이 없습니다.'
});
}
if (access_level || worker_id || is_active !== undefined) {
return res.status(403).json({
if (access_level || is_active !== undefined) {
return res.status(403).json({
success: false,
error: '권한, 작업자 ID, 활성화 상태는 관리자만 수정할 수 있습니다.'
error: '권한, 활성화 상태는 관리자만 수정할 수 있습니다.'
});
}
}
@@ -744,11 +739,6 @@ router.put('/users/:id', verifyToken, async (req, res) => {
updateValues.push(access_level);
}
if (worker_id !== undefined && ['admin', 'system'].includes(req.user.access_level)) {
updateFields.push('worker_id = ?');
updateValues.push(worker_id || null);
}
if (is_active !== undefined && ['admin', 'system'].includes(req.user.access_level)) {
updateFields.push('is_active = ?');
updateValues.push(is_active);
@@ -794,7 +784,7 @@ router.put('/users/:id', verifyToken, async (req, res) => {
// 업데이트된 사용자 정보 조회
const [updated] = await connection.execute(
'SELECT user_id, username, name, email, access_level, worker_id, is_active FROM users WHERE user_id = ?',
'SELECT user_id, username, name, email, access_level, is_active FROM users WHERE user_id = ?',
[userId]
);