feat: refactor code

This commit is contained in:
2026-06-17 12:57:28 +03:30
parent e9f294fa19
commit 519752b478
106 changed files with 3459 additions and 2309 deletions
+28
View File
@@ -0,0 +1,28 @@
/// تبدیل کد وضعیت HTTP و پیام سرور به پیام خطای فارسیِ قابل‌نمایش.
String errorConvertor(int? statusCode, String? message) {
if (statusCode != null) {
switch (statusCode) {
case 500:
return 'ارتباط با سرور برقرار نشد؛ کمی بعد دوباره تلاش کنید';
case 429:
return 'تعداد درخواست‌ها زیاد است؛ کمی بعد دوباره تلاش کنید';
case 401:
return 'دسترسی لازم را ندارید؛ دوباره وارد شوید';
case 403:
return 'این درخواست مجاز نیست';
case 404:
return message ?? 'یافت نشد';
case 409:
return message ?? 'قبلاً انجام شده است';
case 402:
return 'موجودی سکه کافی نیست';
case 400:
return message ?? 'درخواست نامعتبر است';
case 422:
return message ?? 'مقادیر وارد شده صحیح نیست';
default:
return message ?? 'خطای نامشخص';
}
}
return message ?? 'خطا در ارتباط با سرور';
}
+103
View File
@@ -0,0 +1,103 @@
import 'package:get_it/get_it.dart';
import '../../feature/auth/data/data_source/local/auth_local_data.dart';
import '../../feature/auth/data/data_source/remote/auth_api_provider.dart';
import '../../feature/auth/data/repository/auth_repository_impl.dart';
import '../../feature/auth/domain/repository/auth_repository.dart';
import '../../feature/auth/domain/use_cases/check_otp_usecase.dart';
import '../../feature/auth/domain/use_cases/login_usecase.dart';
import '../../feature/auth/domain/use_cases/logout_usecase.dart';
import '../../feature/auth/domain/use_cases/update_profile_usecase.dart';
import '../../feature/auth/presentation/bloc/auth_bloc.dart';
import '../../feature/game/data/data_source/remote/game_api_provider.dart';
import '../../feature/game/data/data_source/remote/game_ws_provider.dart';
import '../../feature/game/data/repository/game_repository_impl.dart';
import '../../feature/game/domain/repository/game_repository.dart';
import '../../feature/game/domain/use_cases/get_tables_info_usecase.dart';
import '../../feature/game/domain/use_cases/get_tiers_usecase.dart';
import '../../feature/game/presentation/bloc/game_bloc.dart';
import '../../feature/game/presentation/bloc/private_info_bloc.dart';
import '../../feature/game/presentation/bloc/tier_bloc.dart';
import '../../feature/profile/data/data_source/remote/profile_api_provider.dart';
import '../../feature/profile/data/repository/profile_repository_impl.dart';
import '../../feature/profile/domain/repository/profile_repository.dart';
import '../../feature/profile/domain/use_cases/get_profile_usecase.dart';
import '../../feature/profile/domain/use_cases/save_profile_usecase.dart';
import '../../feature/profile/presentation/bloc/profile_bloc.dart';
import '../../feature/shop/data/data_source/remote/shop_api_provider.dart';
import '../../feature/shop/data/repository/shop_repository_impl.dart';
import '../../feature/shop/domain/repository/shop_repository.dart';
import '../../feature/shop/domain/use_cases/ad_reward_usecase.dart';
import '../../feature/shop/domain/use_cases/buy_card_usecase.dart';
import '../../feature/shop/domain/use_cases/get_shop_usecase.dart';
import '../../feature/shop/domain/use_cases/purchase_usecase.dart';
import '../../feature/shop/domain/use_cases/select_card_usecase.dart';
import '../../feature/shop/presentation/bloc/shop_bloc.dart';
import '../../feature/wallet/data/data_source/remote/wallet_api_provider.dart';
import '../../feature/wallet/data/repository/wallet_repository_impl.dart';
import '../../feature/wallet/domain/repository/wallet_repository.dart';
import '../../feature/wallet/domain/use_cases/claim_daily_usecase.dart';
import '../../feature/wallet/domain/use_cases/get_wallet_usecase.dart';
import '../../feature/wallet/presentation/bloc/wallet_bloc.dart';
import '../network/api_provider_imp.dart';
import '../storage/token_storage.dart';
final GetIt locator = GetIt.instance;
/// ثبتِ همه‌ی وابستگی‌ها (تک‌خط در main فراخوانی می‌شود).
Future<void> setupLocator() async {
// --- core ---
locator.registerSingleton<TokenStorage>(TokenStorage());
locator.registerSingleton<ApiProviderImp>(ApiProviderImp(locator()));
// --- data sources ---
locator.registerSingleton<AuthLocalData>(AuthLocalData(locator()));
locator.registerSingleton<AuthApiProvider>(AuthApiProvider());
locator.registerSingleton<WalletApiProvider>(WalletApiProvider());
locator.registerSingleton<ShopApiProvider>(ShopApiProvider());
locator.registerSingleton<ProfileApiProvider>(ProfileApiProvider());
locator.registerSingleton<GameApiProvider>(GameApiProvider());
locator.registerSingleton<GameWsProvider>(GameWsProvider(locator()));
// --- repositories ---
locator.registerSingleton<AuthRepository>(
AuthRepositoryImpl(locator(), locator()));
locator.registerSingleton<WalletRepository>(
WalletRepositoryImpl(locator()));
locator.registerSingleton<ShopRepository>(ShopRepositoryImpl(locator()));
locator.registerSingleton<ProfileRepository>(
ProfileRepositoryImpl(locator()));
locator.registerSingleton<GameRepository>(
GameRepositoryImpl(locator(), locator()));
// --- use cases ---
locator.registerSingleton<LoginUseCase>(LoginUseCase(locator()));
locator.registerSingleton<CheckOtpUseCase>(CheckOtpUseCase(locator()));
locator.registerSingleton<UpdateProfileUseCase>(
UpdateProfileUseCase(locator()));
locator.registerSingleton<LogoutUseCase>(LogoutUseCase(locator()));
locator.registerSingleton<GetWalletUseCase>(GetWalletUseCase(locator()));
locator.registerSingleton<ClaimDailyUseCase>(ClaimDailyUseCase(locator()));
locator.registerSingleton<GetShopUseCase>(GetShopUseCase(locator()));
locator.registerSingleton<BuyCardUseCase>(BuyCardUseCase(locator()));
locator.registerSingleton<SelectCardUseCase>(SelectCardUseCase(locator()));
locator.registerSingleton<PurchaseUseCase>(PurchaseUseCase(locator()));
locator.registerSingleton<AdRewardUseCase>(AdRewardUseCase(locator()));
locator.registerSingleton<GetProfileUseCase>(GetProfileUseCase(locator()));
locator.registerSingleton<SaveProfileUseCase>(SaveProfileUseCase(locator()));
locator.registerSingleton<GetTiersUseCase>(GetTiersUseCase(locator()));
locator.registerSingleton<GetTablesInfoUseCase>(
GetTablesInfoUseCase(locator()));
// --- blocs (factory) ---
locator.registerFactory<AuthBloc>(
() => AuthBloc(locator(), locator(), locator(), locator()));
locator.registerFactory<WalletBloc>(() => WalletBloc(locator(), locator()));
locator.registerFactory<ShopBloc>(() =>
ShopBloc(locator(), locator(), locator(), locator(), locator()));
locator.registerFactory<ProfileBloc>(
() => ProfileBloc(locator(), locator()));
locator.registerFactory<GameBloc>(() => GameBloc(locator()));
locator.registerFactory<TierBloc>(() => TierBloc(locator()));
locator.registerFactory<PrivateInfoBloc>(() => PrivateInfoBloc(locator()));
}
-27
View File
@@ -1,27 +0,0 @@
import 'package:dio/dio.dart';
import '../config.dart';
import '../storage/token_storage.dart';
/// کلاینت HTTP با تزریق خودکار توکن Bearer.
class ApiClient {
final Dio dio;
ApiClient(TokenStorage storage)
: dio = Dio(BaseOptions(
baseUrl: AppConfig.apiUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {'Accept': 'application/json'},
)) {
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) async {
final token = await storage.read();
if (token != null && token.isNotEmpty) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
},
));
}
}
+41
View File
@@ -0,0 +1,41 @@
import 'package:dio/dio.dart';
import '../config.dart';
import '../storage/token_storage.dart';
/// لایه‌ی پایه‌ی شبکه: یک Dio با baseUrl، تزریق خودکار توکن Bearer و
/// validateStatus باز (تا کدهای خطا throw نشوند و در repository بررسی شوند).
class ApiProviderImp {
final Dio dio;
ApiProviderImp(TokenStorage storage)
: dio = Dio(BaseOptions(
baseUrl: AppConfig.apiUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 15),
headers: {'Accept': 'application/json'},
validateStatus: (_) => true,
)) {
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) async {
final token = await storage.read();
if (token != null && token.isNotEmpty) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
},
));
}
Future<Response> get(String path, {Map<String, dynamic>? query}) =>
dio.get(path, queryParameters: query);
Future<Response> post(String path, {Object? body}) =>
dio.post(path, data: body);
Future<Response> put(String path, {Object? body}) =>
dio.put(path, data: body);
Future<Response> delete(String path, {Object? body}) =>
dio.delete(path, data: body);
}
+15
View File
@@ -0,0 +1,15 @@
/// نتیجه‌ی یک عملیات داده‌ای: موفق (داده) یا خطا (پیام).
/// لایه‌ی data همیشه DataState برمی‌گرداند تا لایه‌های بالا throw نگیرند.
abstract class DataState<T> {
final T? data;
final String? error;
const DataState(this.data, this.error);
}
class DataSuccess<T> extends DataState<T> {
const DataSuccess(T data) : super(data, null);
}
class DataError<T> extends DataState<T> {
const DataError(String error) : super(null, error);
}
+37
View File
@@ -0,0 +1,37 @@
/// قرارداد یوزکیس: هر یوزکیس با یک پارامتر فراخوانی می‌شود و یک Future برمی‌گرداند.
abstract class UseCase<T, P> {
Future<T> call(P params);
}
/// نبودِ پارامتر (برای یوزکیس‌های بدون ورودی).
class NoParams {
const NoParams();
}
/// پارامتر بررسی کد یک‌بارمصرف.
class OtpParams {
final String mobile;
final String token;
const OtpParams(this.mobile, this.token);
}
/// پارامتر به‌روزرسانی پروفایل (نام و آواتار).
class ProfileParams {
final String firstName;
final String avatar;
const ProfileParams(this.firstName, this.avatar);
}
/// پارامتر خرید درون‌برنامه‌ای (IAP).
class PurchaseParams {
final String store;
final String kind;
final String productId;
final String token;
const PurchaseParams({
required this.store,
required this.kind,
required this.productId,
required this.token,
});
}