You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
12 KiB
12 KiB
راهنمای فنی مهاجرت دریافت و کش احادیث به دیتابیس SQLite آفلاین (Single-File Architecture)
مخاطب: توسعهدهنده فلاتر (تیم فرانتاند / موبایل)
هدف: حذف پردازش سنگین جیسون احادیث (بیش از ۳۳ مگابایت)، حذف کرش و هنگ رم در Hive، و پیادهسازی دریافت مستقیم فایل فشرده SQLite دیتابیس احادیث و تفکیک تفاسیر.
۱. خلاصه تغییرات و چرایی مهاجرت
مشکل قبلی:
- دانلود اندپوینت
/api/hadis/sync/hadis/حجمی معادل ۳۳ مگابایت جیسون خام داشت. - پارس کردن این حجم از جیسون و اینسرت در Hive باعث افت فریم، فریز شدن UI و مصرف بیش از ۲۰۰ مگابایت رم در گوشی میشد.
- تفاسیر دستهبندیها به ازای تکتک احادیث درون جیسون تکرار میشد (یک متن طولانی تفسیر ۱۰ بار در ۱۰ حدیث مختلف یک دسته کپی میشد!).
معماری جدید (سرور جنگو):
- فایل واحد دیتابیس آماده: سرور یک فایل پایگاهداده بهینه، ایندکسشده و فشرده با Gzip به نام
hadis_offline.sqlite.gz(حجم حدود ۲ تا ۴ مگابایت) آماده کرده است که مستقیماً از طریق Nginx دانلود میشود. - تفکیک کامل تفاسیر: تفاسیر دستهبندی در یک جدول مجزا قرار گرفتهاند و متنهای تکراری حذف شدهاند.
- اندپوینت نسخه پایدار: اپلیکیشن نسخه دیتابیس محلی خود را با سرور مقایسه میکند و فقط در صورت تغییر نسخه، فایل دیتابیس را دانلود و جایگزین میکند.
۲. اندپوینتهای جدید و بهروزشده بکاند
الف) اندپوینت اختصاصی دریافت نسخه دیتابیس SQLite:
GET /api/hadis/sync/database/
پاسخ نمونه:
{
"version": 2,
"download_url": "https://dovodi.online/media/hadis_db/hadis_offline.sqlite.gz",
"file_size": 2845620,
"checksum_md5": "d41d8cd98f00b204e9800998ecf8427e",
"hadis_count": 1050,
"categories_count": 48,
"is_active": true,
"notes": "بهروزرسانی احادیث ماه مبارک",
"created_at": "2026-09-10T10:15:00Z"
}
ب) اندپوینت قبلی نسخهبندی (Backward Compatible):
اندپوینت /api/hadis/sync/version/ نیز حفظ شده و کلید sqlite_database را در بدنه خود برمیگرداند تا در صورتی که ساختار فعلی فلاتر به آن متصل است، کدی شکسته نشود.
۳. ساختار جداول درون فایل SQLite (hadis_offline.sqlite)
فایل دیتابیس دانلود شده شامل جداول استاندارد زیر است:
۱. جدول hadiths (احادیث)
| نام ستون | نوع داده | توضیحات |
|---|---|---|
id |
INTEGER (PK) | شناسه حدیث |
slug |
TEXT (INDEX) | اسلاگ یکتای حدیث |
category_id |
INTEGER (INDEX) | شناسه دستهبندی |
category_slug |
TEXT | اسلاگ دسته |
title |
TEXT | عنوان چندزبانه (JSON) |
title_narrator |
TEXT | راوی عنوان (JSON) |
text |
TEXT | متن عربی حدیث |
translation_json |
TEXT | لیست ترجمهها (JSON) |
address |
TEXT | آدرس منبع حدیث (JSON) |
share_link |
TEXT | لینک اشتراکگذاری |
hadis_status_id |
INTEGER | شناسه وضعیت |
status_title |
TEXT | عنوان وضعیت |
status_color |
TEXT | رنگ وضعیت |
status_main_color_code |
TEXT | کد رنگ اصلی |
status_text |
TEXT | متن وضعیت |
tags_json |
TEXT | لیست تگها |
references_json |
TEXT | لیست منابع و کتابها |
reference_images_json |
TEXT | تصاویر منابع |
narrators_json |
TEXT | سلسله راویان (Transmitters) |
explanations_json |
TEXT | توضیحات و شروح |
۲. جدول category_interpretations (تفاسیر دستهبندی - تفکیکشده)
| نام ستون | نوع داده | توضیحات |
|---|---|---|
id |
INTEGER (PK) | شناسه تفسیر |
category_id |
INTEGER (INDEX) | شناسه دستهبندی مربوطه |
title |
TEXT | عنوان تفسیر |
slug |
TEXT | اسلاگ تفسیر |
narrator |
TEXT | راوی تفسیر |
text |
TEXT | متن تفصیلی تفسیر |
translation |
TEXT | ترجمه |
references_json |
TEXT | کتابها و منابع تفسیر |
links_json |
TEXT | پیوندها |
۳. جدول hadith_corrections (تصحیحات حدیث)
| نام ستون | نوع داده | توضیحات |
|---|---|---|
id |
INTEGER (PK) | شناسه تصحیح |
hadis_id |
INTEGER (INDEX) | شناسه حدیث متصل |
title |
TEXT | عنوان |
slug |
TEXT | اسلاگ |
narrator |
TEXT | راوی |
description |
TEXT | شرح |
translation |
TEXT | ترجمه |
references_json |
TEXT | منابع تصحیح |
images_json |
TEXT | تصاویر مدارک |
links_json |
TEXT | پیوندها |
۴. جدول categories (دستهها)
شامل id, title, slug, source_type, sect_type.
۴. چرخه سناریوی دریافت و کش در فلاتر (Implementation Flow)
[Start App]
│
▼
1. فراخوانی GET /api/hadis/sync/database/
│
▼
2. آیا دیتابیس محلی وجود ندارد یا version سرور > version ذخیره شده در SharedPreferences است؟
├─► خیر: مستقیماً از SQLite محلی لود کن (آفلاین ۱۰۰٪ در ۰ ثانیه).
│
└─► بله:
3. نمایش لودینگ / پروگرسبار دانلود دیتابیس
4. دانلود فایل hadis_offline.sqlite.gz از Nginx با Dio (با قابلیت onReceiveProgress)
5. بررسی سلامت فایل با هش MD5 (اختیاری ولی توصیه شده)
6. آنزیپ کردن (decompress) فایل Gzip به hadis.sqlite در دایرکتوری getDatabasesPath()
7. پاک کردن فایل موقت .gz
8. ذخیره شماره نسخه جدید در SharedPreferences
9. رفرش کردن کوبیتها و آمادهسازی نمایش
۵. راهنمای گامبهگام پیادهسازی کدهای فلاتر
گام اول: وابستگیهای مورد نیاز در pubspec.yaml
dependencies:
sqflite: ^2.3.3+1
path: ^1.9.0
path_provider: ^2.1.3
archive: ^3.6.1 # برای باز کردن فایل Gzip
crypto: ^3.0.3 # برای بررسی هش MD5
گام دوم: ایجاد کلاس مدیریت دیتابیس SQLite
یک فایل در مسیر lib/src/features/dobodbi/hadith/data/datasources/local/hadith_database_helper.dart ایجاد کنید:
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:archive/archive.dart';
class HadithDatabaseHelper {
static const String _dbFileName = 'hadis.sqlite';
static Database? _database;
static Future<Database> get database async {
if (_database != null && _database!.isOpen) return _database!;
_database = await _initDb();
return _database!;
}
static Future<String> getDatabasePath() async {
final dbFolder = await getDatabasesPath();
return join(dbFolder, _dbFileName);
}
static Future<Database> _initDb() async {
final path = await getDatabasePath();
return await openDatabase(path, readOnly: true);
}
/// اکسترکت فایل فشرده دانلود شده مستقیماً در دایرکتوری دیتابیس
static Future<void> replaceDatabaseWithGzip(File gzFile) async {
// اگر دیتابیس باز است، ابتدا بسته شود
if (_database != null && _database!.isOpen) {
await _database!.close();
_database = null;
}
final targetPath = await getDatabasePath();
final bytes = await gzFile.readAsBytes();
// Gzip decode
final decompressedData = GZipDecoder().decodeBytes(bytes);
// نوشتن فایل دیتابیس نهایی
final targetFile = File(targetPath);
await targetFile.writeAsBytes(decompressedData, flush: true);
// راهاندازی مجدد اتصال
_database = await openDatabase(targetPath, readOnly: true);
}
}
گام سوم: سرویس دانلود و همگامسازی (Sync Manager)
در hadith_datasource.dart متد دانلود اضافه کنید:
Future<void> syncHadithDatabase({
required String downloadUrl,
required int targetVersion,
required Function(int received, int total) onProgress,
}) async {
final tempDir = await getTemporaryDirectory();
final tempGzPath = '${tempDir.path}/hadis_temp.sqlite.gz';
final tempFile = File(tempGzPath);
// دانلود مستقیم فایل با قابلیت گزارش پیشرفت
await _dio.download(
downloadUrl,
tempGzPath,
onReceiveProgress: onProgress,
);
// اکسترکت در دیتابیس
await HadithDatabaseHelper.replaceDatabaseWithGzip(tempFile);
// حذف فایل تمپ
if (await tempFile.exists()) {
await tempFile.delete();
}
// ذخیره نسخه جدید در حافظه محلی
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('hadith_db_version', targetVersion);
}
گام چهارم: کوئری گرفتن احادیث و تفاسیر از SQLite
در دیتاسورس محلی (HadithLocalDataSource):
۱. دریافت احادیث یک دستهبندی با صفحهبندی فوقسریع:
Future<List<HadithModel>> getHadithsByCategory(int categoryId, {int limit = 20, int offset = 0}) async {
final db = await HadithDatabaseHelper.database;
final results = await db.query(
'hadiths',
where: 'category_id = ?',
whereArgs: [categoryId],
limit: limit,
offset: offset,
);
return results.map((row) => _mapRowToHadithModel(row)).toList();
}
۲. دریافت تفاسیر دستهبندی (تفکیکشده):
Future<List<HadithInterpretationModel>> getCategoryInterpretations(int categoryId) async {
final db = await HadithDatabaseHelper.database;
final results = await db.query(
'category_interpretations',
where: 'category_id = ?',
whereArgs: [categoryId],
);
return results.map((row) => HadithInterpretationModel.fromSqliteRow(row)).toList();
}
۳. جستجوی فوقسریع متنی (Full Text Search):
Future<List<HadithModel>> searchHadiths(String keyword) async {
final db = await HadithDatabaseHelper.database;
final results = await db.rawQuery('''
SELECT * FROM hadiths
WHERE text LIKE ? OR translation_json LIKE ?
LIMIT 50
''', ['%$keyword%', '%$keyword%']);
return results.map((row) => _mapRowToHadithModel(row)).toList();
}
۶. مزایای این تغییر برای اپ فلاتر
- کاهش مصرف اینترنت: حجم دانلود کل دیتابیس از ۳۳ مگابایت به کمتر از ۳ مگابایت میرسد (۹۰٪ صرفهجویی).
- زمان سینک صفر: دیگر نیازی به پارس کردن حلقهای جیسون در فلاتر و ایجاد آبجکتهای Hive نیست؛ به محض Unzip شدن، دیتابیس بلافاصله آماده استفاده است.
- مصرف رم بسیار ناچیز: به جای نگهداری تمام احادیث در رم (Boxهای Hive)، دادهها با صفحهبندی (
LIMITوOFFSET) بر اساس نیاز اسکرول کاربر از دیسک خوانده میشوند. - توقف کامل کرشهای دیتابیس آفلاین در نسخههای ضعیف اندروید/iOS.