182 lines
5.5 KiB
Dart
182 lines
5.5 KiB
Dart
import 'dart:async';
|
|
import 'package:dio/dio.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import '../config/api_config.dart';
|
|
import '../utils/web_storage.dart';
|
|
|
|
abstract class PublicHttpClient {
|
|
Future<Response<dynamic>> get(String path, {Map<String, dynamic>? headers});
|
|
Future<Response<dynamic>> post(String path, {dynamic data, Map<String, dynamic>? headers});
|
|
Future<Response<dynamic>> put(String path, {dynamic data, Map<String, dynamic>? headers});
|
|
}
|
|
|
|
class DioPublicHttpClient implements PublicHttpClient {
|
|
final Dio _dio;
|
|
DioPublicHttpClient({Dio? dio}) : _dio = dio ?? Dio(BaseOptions(
|
|
baseUrl: ApiConfig.baseUrl,
|
|
connectTimeout: const Duration(seconds: 10),
|
|
receiveTimeout: const Duration(seconds: 10),
|
|
contentType: 'application/json',
|
|
));
|
|
|
|
@override
|
|
Future<Response> get(String path, {Map<String, dynamic>? headers}) {
|
|
return _dio.get(path, options: Options(headers: headers));
|
|
}
|
|
|
|
@override
|
|
Future<Response> post(String path, {data, Map<String, dynamic>? headers}) {
|
|
return _dio.post(path, data: data ?? {}, options: Options(headers: headers));
|
|
}
|
|
|
|
@override
|
|
Future<Response> put(String path, {data, Map<String, dynamic>? headers}) {
|
|
return _dio.put(path, data: data ?? {}, options: Options(headers: headers));
|
|
}
|
|
}
|
|
|
|
class PublicEventService {
|
|
final PublicHttpClient _http;
|
|
PublicEventService() : _http = DioPublicHttpClient();
|
|
@visibleForTesting
|
|
PublicEventService.forTest(this._http);
|
|
|
|
static String tokenKey(String publicHash) => 'event_token_' + publicHash;
|
|
|
|
String? getSavedToken(String publicHash) {
|
|
return WebStorage.getItem(tokenKey(publicHash));
|
|
}
|
|
|
|
void saveToken(String publicHash, String token) {
|
|
WebStorage.setItem(tokenKey(publicHash), token);
|
|
}
|
|
|
|
void removeToken(String publicHash) {
|
|
WebStorage.removeItem(tokenKey(publicHash));
|
|
}
|
|
|
|
Map<String, dynamic>? _authHeader(String? token) {
|
|
if (token == null || token.isEmpty) return null;
|
|
return { 'Authorization': 'Bearer ' + token };
|
|
}
|
|
|
|
Future<Map<String, dynamic>> fetchEvent(String publicHash) async {
|
|
final saved = getSavedToken(publicHash);
|
|
// 백엔드는 GET이 아니라 POST('/:publicHash')로 최초 진입/토큰 인증을 처리함
|
|
final res = await _http.post(
|
|
'/public/' + publicHash,
|
|
data: {},
|
|
headers: _authHeader(saved),
|
|
);
|
|
final data = res.data as Map<String, dynamic>;
|
|
// 응답에 token이 있으면 저장 (토큰 재발급 케이스)
|
|
final token = data['token']?.toString();
|
|
if (token != null && token.isNotEmpty) {
|
|
saveToken(publicHash, token);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
Future<Map<String, dynamic>> enter(String publicHash, {String? password}) async {
|
|
final saved = getSavedToken(publicHash);
|
|
try {
|
|
// 우선 저장된 토큰으로 시도, 실패 시 비밀번호로 재시도는 서버가 처리
|
|
final res = await _http.post(
|
|
'/public/' + publicHash,
|
|
data: password != null ? { 'password': password } : {},
|
|
headers: _authHeader(saved),
|
|
);
|
|
final data = res.data as Map<String, dynamic>;
|
|
// 응답에 token이 있으면 저장
|
|
final token = data['token']?.toString();
|
|
if (token != null && token.isNotEmpty) {
|
|
saveToken(publicHash, token);
|
|
}
|
|
return data;
|
|
} on DioException catch (e) {
|
|
final status = e.response?.statusCode;
|
|
if (status == 401 || status == 403) {
|
|
removeToken(publicHash);
|
|
}
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
Future<void> addOrUpdateParticipant(String publicHash, {
|
|
String? participantId,
|
|
required String memberId,
|
|
required String status,
|
|
required String paymentStatus,
|
|
String? comment,
|
|
}) async {
|
|
final token = getSavedToken(publicHash);
|
|
final payload = {
|
|
if (participantId != null) 'participantId': participantId,
|
|
'memberId': memberId,
|
|
'status': status,
|
|
'paymentStatus': paymentStatus,
|
|
if (comment != null) 'comment': comment,
|
|
};
|
|
await _http.post(
|
|
'/public/' + publicHash + '/participant',
|
|
data: payload,
|
|
headers: _authHeader(token),
|
|
);
|
|
}
|
|
|
|
Future<void> addGuest(String publicHash, {
|
|
required String name,
|
|
String? phone,
|
|
String? gender,
|
|
num? average,
|
|
}) async {
|
|
final token = getSavedToken(publicHash);
|
|
final payload = {
|
|
'name': name,
|
|
if (phone != null) 'phone': phone,
|
|
if (gender != null) 'gender': gender,
|
|
if (average != null) 'average': average,
|
|
};
|
|
await _http.post(
|
|
'/public/' + publicHash + '/guest',
|
|
data: payload,
|
|
headers: _authHeader(token),
|
|
);
|
|
}
|
|
|
|
Future<void> updateScore(String publicHash, {
|
|
required String participantId,
|
|
required int gameNumber,
|
|
required int score,
|
|
int? handicap,
|
|
dynamic teamNumber,
|
|
}) async {
|
|
final token = getSavedToken(publicHash);
|
|
final payload = {
|
|
'participantId': participantId,
|
|
'gameNumber': gameNumber,
|
|
'score': score,
|
|
if (handicap != null) 'handicap': handicap,
|
|
if (teamNumber != null) 'teamNumber': teamNumber,
|
|
};
|
|
await _http.put(
|
|
'/public/' + publicHash + '/score',
|
|
data: payload,
|
|
headers: _authHeader(token),
|
|
);
|
|
}
|
|
|
|
Future<void> updateTeamHandicap(String publicHash, {
|
|
required int teamNumber,
|
|
required int handicap,
|
|
}) async {
|
|
final token = getSavedToken(publicHash);
|
|
await _http.put(
|
|
'/public/' + publicHash + '/team/' + teamNumber.toString() + '/handicap',
|
|
data: { 'teamNumber': teamNumber, 'handicap': handicap },
|
|
headers: _authHeader(token),
|
|
);
|
|
}
|
|
}
|