Files
waiting-system/routers/users.py
Jun-dev f699a29a85 Add waiting system application files
- Add main application files (main.py, models.py, schemas.py, etc.)
- Add routers for all features (waiting, attendance, members, etc.)
- Add HTML templates for admin and user interfaces
- Add migration scripts and utility files
- Add Docker configuration
- Add documentation files
- Add .gitignore to exclude database and cache files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 00:29:39 +09:00

310 lines
9.0 KiB
Python

"""
사용자 관리 라우터
- 사용자 목록 조회
- 사용자 생성
- 사용자 상세 조회
- 사용자 수정
- 사용자 비활성화
"""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from datetime import datetime
from typing import List
from database import get_db
from models import User, Store
from schemas import User as UserSchema, UserCreate, UserUpdate
from auth import get_current_user, require_franchise_admin, get_password_hash
router = APIRouter()
@router.get("/", response_model=List[UserSchema])
async def get_users(
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""사용자 목록 조회
프랜차이즈 관리자만 접근 가능
자신의 프랜차이즈에 속한 사용자만 조회
Returns:
List[User]: 프랜차이즈의 모든 사용자 목록
"""
users = db.query(User).filter(
User.franchise_id == current_user.franchise_id
).order_by(User.created_at.desc()).all()
return users
@router.post("/", response_model=UserSchema, status_code=status.HTTP_201_CREATED)
async def create_user(
user_create: UserCreate,
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""사용자 생성
프랜차이즈 관리자만 접근 가능
Args:
user_create: 생성할 사용자 정보
Returns:
User: 생성된 사용자 정보
"""
# 사용자명 중복 체크
existing_user = db.query(User).filter(User.username == user_create.username).first()
if existing_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="이미 존재하는 사용자명입니다"
)
# 역할 검증
if user_create.role not in ['franchise_admin', 'store_admin', 'franchise_manager']:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="올바르지 않은 역할입니다."
)
# 매장 관리자인 경우 매장 ID 필수
if user_create.role == 'store_admin' and not user_create.store_id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="매장 관리자는 매장 ID가 필요합니다"
)
# 매장 ID가 있는 경우 해당 매장이 자신의 프랜차이즈에 속하는지 확인
if user_create.store_id:
store = db.query(Store).filter(
Store.id == user_create.store_id,
Store.franchise_id == current_user.franchise_id
).first()
if not store:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="매장을 찾을 수 없거나 접근 권한이 없습니다"
)
# 사용자 생성
password_hash = get_password_hash(user_create.password)
new_user = User(
username=user_create.username,
password_hash=password_hash,
role=user_create.role,
franchise_id=current_user.franchise_id,
store_id=user_create.store_id,
is_active=True
)
# 중간 관리자의 매장 권한 설정
if user_create.role == 'franchise_manager' and user_create.managed_store_ids:
stores = db.query(Store).filter(
Store.id.in_(user_create.managed_store_ids),
Store.franchise_id == current_user.franchise_id
).all()
if len(stores) != len(user_create.managed_store_ids):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="일부 매장을 찾을 수 없거나 접근 권한이 없습니다"
)
new_user.managed_stores = stores
db.add(new_user)
db.commit()
db.refresh(new_user)
return new_user
@router.get("/{user_id}", response_model=UserSchema)
async def get_user(
user_id: int,
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""사용자 상세 조회
Args:
user_id: 사용자 ID
Returns:
User: 사용자 상세 정보
"""
user = db.query(User).filter(
User.id == user_id,
User.franchise_id == current_user.franchise_id
).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="사용자를 찾을 수 없습니다"
)
return user
@router.put("/{user_id}", response_model=UserSchema)
async def update_user(
user_id: int,
user_update: UserUpdate,
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""사용자 정보 수정
Args:
user_id: 사용자 ID
user_update: 수정할 사용자 정보
Returns:
User: 수정된 사용자 정보
"""
user = db.query(User).filter(
User.id == user_id,
User.franchise_id == current_user.franchise_id
).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="사용자를 찾을 수 없습니다"
)
# 사용자명 변경 시 중복 체크
if user_update.username and user_update.username != user.username:
existing_user = db.query(User).filter(User.username == user_update.username).first()
if existing_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="이미 존재하는 사용자명입니다"
)
# 역할 변경 시 검증
if user_update.role and user_update.role not in ['franchise_admin', 'store_admin', 'franchise_manager']:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="올바르지 않은 역할입니다."
)
# 매장 ID 변경 시 검증
if user_update.store_id:
store = db.query(Store).filter(
Store.id == user_update.store_id,
Store.franchise_id == current_user.franchise_id
).first()
if not store:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="매장을 찾을 수 없거나 접근 권한이 없습니다"
)
# 수정
update_data = user_update.dict(exclude_unset=True, exclude={'password', 'managed_store_ids'})
for key, value in update_data.items():
setattr(user, key, value)
# 중간 관리자 매장 권한 업데이트
if user_update.role == 'franchise_manager' or (not user_update.role and user.role == 'franchise_manager'):
if user_update.managed_store_ids is not None:
stores = db.query(Store).filter(
Store.id.in_(user_update.managed_store_ids),
Store.franchise_id == current_user.franchise_id
).all()
user.managed_stores = stores
elif user_update.role and user_update.role != 'franchise_manager':
# 다른 역할로 변경 시 관리 매장 초기화
user.managed_stores = []
# 비밀번호 변경이 있는 경우
if user_update.password:
user.password_hash = get_password_hash(user_update.password)
user.updated_at = datetime.now()
db.commit()
db.refresh(user)
return user
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(
user_id: int,
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""사용자 비활성화
실제로 삭제하지 않고 is_active를 False로 변경
자기 자신은 비활성화할 수 없음
Args:
user_id: 사용자 ID
"""
# 자기 자신은 비활성화할 수 없음
if user_id == current_user.id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="자기 자신을 비활성화할 수 없습니다"
)
user = db.query(User).filter(
User.id == user_id,
User.franchise_id == current_user.franchise_id
).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="사용자를 찾을 수 없습니다"
)
user.is_active = False
user.updated_at = datetime.now()
db.commit()
@router.post("/{user_id}/activate", response_model=UserSchema)
async def activate_user(
user_id: int,
current_user: User = Depends(require_franchise_admin),
db: Session = Depends(get_db)
):
"""사용자 활성화
Args:
user_id: 사용자 ID
Returns:
User: 활성화된 사용자 정보
"""
user = db.query(User).filter(
User.id == user_id,
User.franchise_id == current_user.franchise_id
).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="사용자를 찾을 수 없습니다"
)
user.is_active = True
user.updated_at = datetime.now()
db.commit()
db.refresh(user)
return user