feat: 사용자 관리 시스템 백엔드 API 호환성 대폭 개선
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled

- UserRemoteDataSource: API v0.2.1 스펙 완전 대응
  • 응답 형식 통일 (success: true 구조)
  • 페이지네이션 처리 개선
  • 에러 핸들링 강화
  • 불필요한 파라미터 제거 (includeInactive 등)

- UserDto 모델 현대화:
  • 서버 응답 구조와 100% 일치
  • 도메인 모델 변환 메서드 추가
  • Freezed 불변성 패턴 완성

- User 도메인 모델 신규 구현:
  • Clean Architecture 원칙 준수
  • UserRole enum 타입 안전성 강화
  • 비즈니스 로직 캡슐화

- 사용자 관련 UseCase 리팩토링:
  • Repository 패턴 완전 적용
  • Either<Failure, Success> 에러 처리
  • 의존성 주입 최적화

- UI 컨트롤러 및 화면 개선:
  • API 응답 변경사항 반영
  • 사용자 권한 표시 정확성 향상
  • 폼 검증 로직 강화

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JiWoong Sul
2025-08-15 23:31:51 +09:00
parent c1063f5670
commit 93bceb8a6c
20 changed files with 2006 additions and 2017 deletions

View File

@@ -1,95 +1,150 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../../models/user_model.dart';
part 'user_dto.freezed.dart';
part 'user_dto.g.dart';
enum UserRole {
@JsonValue('admin')
admin,
@JsonValue('manager')
manager,
@JsonValue('member')
member,
}
/// 사용자 데이터 전송 객체 (서버 API v0.2.1 대응)
/// GET /api/v1/users/{id} 응답 형태
@freezed
class UserDto with _$UserDto {
const UserDto._();
const factory UserDto({
/// 사용자 ID (자동 생성)
required int id,
/// 사용자명 (유니크, 필수)
required String username,
/// 이름 (필수)
required String name,
String? email,
/// 이메일 (유니크, 필수)
required String email,
/// 전화번호 (선택)
String? phone,
/// 권한 (admin, manager, staff)
required String role,
@JsonKey(name: 'company_id') int? companyId,
@JsonKey(name: 'company_name') String? companyName,
@JsonKey(name: 'branch_id') int? branchId,
@JsonKey(name: 'branch_name') String? branchName,
/// 활성화 상태 (기본값: true)
@JsonKey(name: 'is_active') required bool isActive,
@JsonKey(name: 'last_login_at') DateTime? lastLoginAt,
/// 생성일시 (자동 입력)
@JsonKey(name: 'created_at') required DateTime createdAt,
@JsonKey(name: 'updated_at') required DateTime updatedAt,
/// 수정일시 (자동 갱신, 선택적)
@JsonKey(name: 'updated_at') DateTime? updatedAt,
}) = _UserDto;
factory UserDto.fromJson(Map<String, dynamic> json) =>
_$UserDtoFromJson(json);
/// DTO를 도메인 모델로 변환
User toDomainModel() {
return User(
id: id,
username: username,
email: email,
name: name,
phone: phone,
role: UserRole.fromString(role),
isActive: isActive,
createdAt: createdAt,
updatedAt: updatedAt,
);
}
}
/// 사용자 생성 요청 DTO (POST /api/v1/users)
@freezed
class CreateUserRequest with _$CreateUserRequest {
const factory CreateUserRequest({
/// 사용자명 (필수, 유니크, 3자 이상)
required String username,
String? email,
/// 이메일 (필수, 유니크, 이메일 형식)
required String email,
/// 비밀번호 (필수, 6자 이상)
required String password,
/// 이름 (필수)
required String name,
/// 전화번호 (선택, "010-1234-5678" 형태)
String? phone,
/// 권한 (필수: admin, manager, staff)
required String role,
@JsonKey(name: 'company_id') int? companyId,
@JsonKey(name: 'branch_id') int? branchId,
}) = _CreateUserRequest;
factory CreateUserRequest.fromJson(Map<String, dynamic> json) =>
_$CreateUserRequestFromJson(json);
/// 도메인 모델에서 생성 요청 DTO로 변환
factory CreateUserRequest.fromDomain(User user, String password) {
return CreateUserRequest(
username: user.username,
email: user.email,
password: password,
name: user.name,
phone: user.phone,
role: user.role.name,
);
}
}
/// 사용자 수정 요청 DTO (PUT /api/v1/users/{id})
@freezed
class UpdateUserRequest with _$UpdateUserRequest {
const factory UpdateUserRequest({
/// 이름 (선택)
String? name,
/// 이메일 (선택, 유니크, 이메일 형식)
String? email,
/// 비밀번호 (선택, 6자 이상)
String? password,
/// 전화번호 (선택)
String? phone,
/// 권한 (선택: admin, manager, staff)
String? role,
@JsonKey(name: 'company_id') int? companyId,
@JsonKey(name: 'branch_id') int? branchId,
@JsonKey(name: 'is_active') bool? isActive,
}) = _UpdateUserRequest;
factory UpdateUserRequest.fromJson(Map<String, dynamic> json) =>
_$UpdateUserRequestFromJson(json);
/// 도메인 모델에서 수정 요청 DTO로 변환
factory UpdateUserRequest.fromDomain(User user, {String? newPassword}) {
return UpdateUserRequest(
name: user.name,
email: user.email,
password: newPassword,
phone: user.phone,
role: user.role.name,
);
}
}
/// 사용자명 중복 확인 응답 DTO
@freezed
class ChangeStatusRequest with _$ChangeStatusRequest {
const factory ChangeStatusRequest({
@JsonKey(name: 'is_active') required bool isActive,
}) = _ChangeStatusRequest;
class CheckUsernameResponse with _$CheckUsernameResponse {
const factory CheckUsernameResponse({
required bool available,
String? message,
}) = _CheckUsernameResponse;
factory ChangeStatusRequest.fromJson(Map<String, dynamic> json) =>
_$ChangeStatusRequestFromJson(json);
}
@freezed
class ChangePasswordRequest with _$ChangePasswordRequest {
const factory ChangePasswordRequest({
@JsonKey(name: 'current_password') required String currentPassword,
@JsonKey(name: 'new_password') required String newPassword,
}) = _ChangePasswordRequest;
factory ChangePasswordRequest.fromJson(Map<String, dynamic> json) =>
_$ChangePasswordRequestFromJson(json);
factory CheckUsernameResponse.fromJson(Map<String, dynamic> json) =>
_$CheckUsernameResponseFromJson(json);
}
/// 사용자 목록 응답 DTO (기존 PaginatedResponse 형태 유지)
@freezed
class UserListDto with _$UserListDto {
const UserListDto._();
@@ -111,8 +166,14 @@ class UserListDto with _$UserListDto {
factory UserListDto.fromJson(Map<String, dynamic> json) =>
_$UserListDtoFromJson(json);
/// DTO 목록을 도메인 모델 목록으로 변환
List<User> toDomainModels() {
return users.map((dto) => dto.toDomainModel()).toList();
}
}
/// 사용자 상세 응답 DTO
@freezed
class UserDetailDto with _$UserDetailDto {
const factory UserDetailDto({
@@ -123,6 +184,7 @@ class UserDetailDto with _$UserDetailDto {
_$UserDetailDtoFromJson(json);
}
/// 일반적인 사용자 API 응답 DTO
@freezed
class UserResponse with _$UserResponse {
const factory UserResponse({