결재 API 계약 보완 및 테스트 정리

This commit is contained in:
JiWoong Sul
2025-10-16 18:53:22 +09:00
parent 9e2244f260
commit efed3c1a6f
44 changed files with 1969 additions and 293 deletions

View File

@@ -0,0 +1,32 @@
import '../../../../core/permissions/permission_manager.dart';
import '../../../../core/permissions/permission_resources.dart';
/// 로그인 응답에서 내려오는 단일 권한(리소스 + 액션 목록)을 표현한다.
class AuthPermission {
const AuthPermission({required this.resource, required this.actions});
/// 서버가 반환한 리소스 식별자 (예: `/stock-transactions`)
final String resource;
/// 허용된 액션 문자열 목록 (예: `view`, `create`)
final List<String> actions;
/// [PermissionManager]가 이해할 수 있는 포맷으로 변환한다.
Map<String, Set<PermissionAction>> toPermissionMap() {
final normalized = PermissionResources.normalize(resource);
final actionSet = <PermissionAction>{};
for (final raw in actions) {
final matched = PermissionAction.values.where(
(action) => action.name == raw.trim().toLowerCase(),
);
if (matched.isEmpty) {
continue;
}
actionSet.addAll(matched);
}
if (actionSet.isEmpty) {
return <String, Set<PermissionAction>>{};
}
return {normalized: actionSet};
}
}

View File

@@ -0,0 +1,31 @@
import 'auth_permission.dart';
import 'authenticated_user.dart';
/// 로그인 또는 토큰 갱신 결과를 표현하는 세션 모델.
class AuthSession {
const AuthSession({
required this.accessToken,
required this.refreshToken,
required this.expiresAt,
required this.user,
this.permissions = const [],
});
/// API 인증에 사용되는 액세스 토큰.
final String accessToken;
/// 토큰 갱신에 사용되는 리프레시 토큰.
final String refreshToken;
/// 액세스 토큰 만료 시각.
final DateTime? expiresAt;
/// 로그인한 사용자 정보.
final AuthenticatedUser user;
/// 사용자에게 할당된 권한 목록.
final List<AuthPermission> permissions;
/// 리프레시 토큰이 유효한지 여부를 단순 판단한다.
bool get hasRefreshToken => refreshToken.isNotEmpty;
}

View File

@@ -0,0 +1,29 @@
/// 로그인 성공 시 반환되는 사용자 정보.
class AuthenticatedUser {
const AuthenticatedUser({
required this.id,
required this.name,
this.employeeNo,
this.email,
this.primaryGroupId,
this.primaryGroupName,
});
/// 사용자 식별자
final int id;
/// 이름
final String name;
/// 사번
final String? employeeNo;
/// 이메일
final String? email;
/// 기본 소속 그룹 ID
final int? primaryGroupId;
/// 기본 소속 그룹명
final String? primaryGroupName;
}

View File

@@ -0,0 +1,21 @@
/// 로그인 요청 값을 표현하는 도메인 모델.
///
/// - identifier: 사번 또는 이메일 등 사용자가 입력한 계정 식별자
/// - password: 평문 비밀번호
/// - rememberMe: 재로그인 시 토큰 유지를 원하는지 여부
class LoginRequest {
const LoginRequest({
required this.identifier,
required this.password,
this.rememberMe = false,
});
/// 사번 또는 이메일
final String identifier;
/// 평문 비밀번호
final String password;
/// 재로그인 시 토큰 지속 여부
final bool rememberMe;
}