hesabixCore/docs/BACKUP_RESTORE_SCENARIO.md
2026-02-08 17:15:17 +00:00

14 KiB
Raw Permalink Blame History

سناریوی پشتیبان‌گیری و بازیابی جامع کسب‌وکار (با فایل‌های الصاقی)

این سند حاصل بررسی پروژه است و فقط سناریو را شرح می‌دهد؛ در این مرحله تغییری در کد اعمال نشده است.


۱. وضعیت فعلی پروژه

۱.۱ خروجی اکسل (پشتیبان فعلی کسب‌وکار)

  • مسیر: BackupController::createBackup → API: POST /api/backup/create
  • خروجی: یک فایل Excel با چندین شیت:
    • اطلاعات کسب و کار، اشخاص، کالاها، حساب‌های بانکی
    • اسناد حسابداری، جدول حساب‌ها، تراکنش‌ها (HesabdariRow)
    • فاکتورهای فروش/خرید، برگشت از خرید/فروش، دریافت/پرداخت اشخاص
    • انبارها
  • محدودیت‌ها:
    • هیچ فایل الصاقی (تصویر، PDF، سند اسکن و …) در این خروجی نیست.
    • هیچ قابلیت بازیابی (Restore) از همین فایل اکسل در سیستم وجود ندارد؛ فقط برای مشاهده/گزارش است.
    • وابسته به افزونه accpro است.

۱.۲ پشتیبان دیتابیس (سیستم)

  • مسیر: DatabaseController → APIهای admin برای backup/upload به FTP
  • خروجی: فایل .sql با mysqldump برای کل دیتابیس (همه کسب‌وکارها).
  • محدودیت‌ها:
    • سطح سیستم/ادمین است، نه سطح یک کسب‌وکار.
    • فایل‌های داخل hesabixArchive را شامل نمی‌شود.

۱.۳ محل و نوع فایل‌های الصاقی

منبع Entity / محل ذخیره مسیر فیزیکی (نسبت به hesabixArchive)
الصاق به حواله انبار ArchiveFile (cat: storeroom_ticket) storage/{bid}/storeroom_attachments/
اسناد پرونده واردات ImportWorkflowDocument.filePath storage/{bid}/import_docs/
آواتار و مهر کسب‌وکار Business.avatar, Business.sealFile avatars/, seal/
  • سرویس FileStorage همه فایل‌های کسب‌وکار را زیر hesabixArchive/storage/{businessId}/{context} ذخیره می‌کند.
  • رکوردهای ArchiveFile با فیلدهای bid, filename (مسیر نسبی), relatedDocType, relatedDocCode به اسناد (مثلاً حواله) وصل می‌شوند.

نتیجه: برای پشتیبان کامل یک کسب‌وکار، علاوه بر داده‌های دیتابیس، باید تمام فایل‌های زیر برای همان bid هم در آرشیو قرار بگیرند:

  • storage/{bid}/
  • در صورت وجود: avatars/{avatar} و seal/{sealFile} مربوط به همان کسب‌وکار

۲. اهداف راهکار پیشنهادی

  1. یک فایل آرشیو واحد برای هر کسب‌وکار که شامل:
    • داده‌های دیتابیس مربوط به همان کسب‌وکار، و
    • همه فایل‌های الصاقی (انبار، پرونده واردات، آواتار، مهر) باشد.
  2. بازیابی در دو حالت:
    • بازگردانی در کسب‌وکار جدید: ایجاد یا انتخاب یک کسب‌وکار خالی و بارگذاری آرشیو روی آن.
    • بازنویسی روی کسب‌وکار فعلی: جایگزینی کامل داده‌ها و فایل‌های همان کسب‌وکار با محتوای آرشیو (با تأیید صریح کاربر).

۳. محتوای پیشنهادی فایل آرشیو

فرمت پیشنهادی: یک فایل ZIP با ساختار ثابت تا نسخه‌های بعدی قابل توسعه باشند.

BusinessBackup_{businessName}_{YYYY-MM-DD_HH-mm}.zip
├── manifest.json          # نسخه فرمت، شناسه و نام کسب‌وکار مبدأ، تاریخ، لیست بخش‌ها
├── data/
│   ├── business.json      # یک رکورد Business (بدون وابستگی به User/سایر جداول)
│   ├── entities/          # هر entity به صورت JSON (یا یک فایل تکی با آرایه‌ها)
│   │   ├── persons.json
│   │   ├── commodities.json
│   │   ├── bank_accounts.json
│   │   ├── hesabdari_table.json
│   │   ├── hesabdari_docs.json
│   │   ├── hesabdari_rows.json
│   │   ├── years.json
│   │   ├── storerooms.json
│   │   ├── storeroom_tickets.json
│   │   ├── storeroom_items.json
│   │   ├── archive_files.json   # متادیتای فایل‌های آرشیو (برای نگاشت مسیر بعد از بازیابی)
│   │   ├── import_workflows.json
│   │   ├── import_workflow_documents.json
│   │   ├── ... (سایر entityهای وابسته به bid)
│   └── registry.json      # رکوردهای Registry که root = شناسه همان کسب‌وکار
└── files/
    └── storage/
        └── {bid}/         # همان ساختار فعلی: storeroom_attachments/, import_docs/, ...
            ├── storeroom_attachments/
            │   └── ...
            └── import_docs/
                └── ...
    └── avatars/           # در صورت استفاده: فایل آواتار این کسب‌وکار
        └── {filename}
    └── seal/
        └── {filename}
  • manifest.json حداقل شامل: version, sourceBusinessId, sourceBusinessName, exportedAt, dataFormatVersion, و در آینده می‌توان checksums یا لیست فایل‌ها را اضافه کرد.
  • ترتیب و وابستگی entityها (مثلاً ابتدا Person و Commodity و Year و HesabdariTable، بعد HesabdariDoc و HesabdariRow و …) در مرحله بازیابی باید رعایت شود تا ارجاع به شناسه‌ها درست باشد.

۴. مراحل پیاده‌سازی پیشنهادی

فاز ۱: تعریف فرمت و سرویس پشتیبان (Export)

  1. تعریف رسمی فرمت آرشیو

    • نسخه فرمت (مثلاً 1.0) و ساختار manifest.json.
    • لیست entityهایی که باید export شوند و ترتیب وابستگی آن‌ها.
  2. سرویس/کلاس Export (مثلاً BusinessArchiveExporter)

    • ورودی: Business $business (یا bid).
    • خروجی: مسیر فایل ZIP موقت یا stream.
    • مراحل:
      • ساخت پوشه/ساختار موقت.
      • نوشتن manifest.json.
      • استخراج تمام entityهای وابسته به این bid از دیتابیس و ذخیره در data/entities/*.json (با حذف یا جایگزینی ارجاعات به entityهای خارج از این کسب‌وکار در صورت نیاز).
      • کپی فایل‌های فیزیکی از hesabixArchive/storage/{bid}/* به داخل files/storage/{bid}/ در ZIP.
      • در صورت وجود، کپی avatars/{avatar} و seal/{sealFile} این کسب‌وکار به داخل ZIP.
      • نوشتن رکوردهای Registry با root = (string)$bid در data/registry.json.
      • فشرده‌سازی به ZIP و برگرداندن فایل یا پاسخ با فایل برای دانلود.
  3. API و UI

    • یک endpoint مثلاً POST /api/backup/archive/create که همان کسب‌وکار جاری کاربر را export کند و فایل ZIP را برگرداند.
    • در صفحه تنظیمات کسب‌وکار (مثلاً تب پشتیبان فعلی)، دکمه «پشتیبان کامل (با فایل‌ها)» که همین API را صدا بزند و ZIP را دانلود کند.

فاز ۲: سرویس بازیابی (Import / Restore)

  1. سرویس اعتبارسنجی آرشیو

    • خواندن ZIP، استخراج manifest.json، بررسی نسخه و یکپارچگی (و در آینده checksum).
    • برگرداندن لیست بخش‌های موجود (کدام entityها و فایل‌ها وجود دارند).
  2. سرویس Restore (مثلاً BusinessArchiveRestorer)

    • ورودی: مسیر فایل ZIP آپلود شده + پارامترهای بازیابی:
      • حالت A – کسب‌وکار جدید:
        یا شناسه کسب‌وکار از پیش ساخته‌شده خالی، یا «ایجاد کسب‌وکار جدید» و سپس پر کردن آن از آرشیو.
      • حالت B – بازنویسی کسب‌وکار فعلی:
        شناسه کسب‌وکار فعلی؛ قبل از بازنویسی حذف/جایگزینی داده‌های قبلی (و در صورت نیاز پشتیبان اضطراری از وضع فعلی).
    • مراحل کلی:
      • استخراج ZIP به پوشه موقت.
      • خواندن ترتیبی entityها از JSON و نگاشت شناسه قدیم → جدید (old bid → new bid، old person id → new person id، و غیره) تا ارجاعات درون آرشیو درست شوند.
      • غیرفعال کردن موقت constraintها یا استفاده از ترتیب درست insert (مثلاً اول Business/Year/Person/Commodity/HesabdariTable، بعد HesabdariDoc، بعد HesabdariRow، و …).
      • وارد کردن رکوردهای Registry با به‌روزرسانی root به شناسه کسب‌وکار مقصد.
      • کپی فایل‌ها از files/storage/{oldBid}/ به hesabixArchive/storage/{newBid}/ و در صورت نیاز به avatars/ و seal/ با نام‌های صحیح.
      • به‌روزرسانی مسیرها در ArchiveFile و ImportWorkflowDocument (و در Business برای avatar/seal) با مسیرهای جدید و شناسه‌های جدید.
      • پاکسازی فایل‌های موقت و در حالت «بازنویسی»، حذف یا آرشیو داده/فایل‌های قبلی آن کسب‌وکار (با سیاستی که تعریف شود).
  3. API و UI

    • آپلود فایل ZIP: مثلاً POST /api/backup/archive/upload که فایل را ذخیره موقت کند و فقط اعتبارسنجی + خلاصه محتوا برگرداند.
    • اجرای بازیابی: مثلاً POST /api/backup/archive/restore با پارامترهای:
      • fileToken (شناسه فایل آپلود شده)
      • mode: new_business | overwrite_business
      • targetBusinessId (در حالت overwrite یا وقتی کسب‌وکار از قبل ساخته شده)
    • در UI:
      • در تنظیمات کسب‌وکار یا صفحه جدا: آپلود ZIP، انتخاب حالت (کسب‌وکار جدید / بازنویسی فعلی)، تأیید و سپس فراخوانی restore.

فاز ۳: امنیت و بهینه‌سازی

  • دسترسی: فقط نقش‌های مجاز (مثلاً settings یا admin) بتوانند export/restore انجام دهند؛ برای overwrite حتماً تأیید دومرحله‌ای یا رمز.
  • حجم و زمان: برای کسب‌وکارهای بزرگ، export می‌تواند به صورت job پس‌زمینه انجام شود و لینک دانلود بعداً به کاربر داده شود؛ restore هم می‌تواند async باشد و وضعیت پیشرفت را گزارش دهد.
  • نسخه‌پذیری: با افزایش entityها یا تغییر ساختار، dataFormatVersion در manifest و کد خواندن نسخه‌های قدیمی (در صورت نیاز) نگه داشته شود.

۵. نکات فنی مهم

  • وابستگی بین entityها: در export باید ترتیب وابستگی رعایت شود (مثلاً Year، Money، Person، Commodity، HesabdariTable، BankAccount، Storeroom، بعد اسناد و ردیف‌ها و حواله و آرشیو و پرونده واردات). در restore هم باید با همان ترتیب و با نگاشت شناسه، insert انجام شود.
  • User و Permission: رکوردهای User و Permission معمولاً جزو «داده یک کسب‌وکار» نیستند یا سیاست جدا دارند؛ در بازیابی روی کسب‌وکار جدید می‌توان کاربر جاری را به عنوان مالک/دسترسی اولیه تنظیم کرد و در حالت overwrite، دسترسی‌های فعلی حفظ یا مطابق سیاست به‌روز شوند.
  • Submitter در ArchiveFile: در export می‌توان شناسه User را نگه داشت؛ در restore به کسب‌وکار جدید اگر آن User در سیستم نباشد، می‌توان به کاربر جاری یا یک کاربر پیش‌فرض نگاشت کرد.
  • Pluginها: وضعیت افزونه‌ها (فعال/غیرفعال، تاریخ انقضا) می‌تواند در آرشیو ذخیره شود؛ در restore به کسب‌وکار جدید معمولاً فقط داده و فایل بازگردانده می‌شود و مجوز افزونه‌ها جداگانه مدیریت شود.

۶. جمع‌بندی

مورد وضع فعلی هدف پیشنهادی
خروجی فقط Excel بدون فایل یک فایل ZIP شامل داده + فایل‌های الصاقی
بازیابی وجود ندارد Restore به کسب‌وکار جدید یا بازنویسی کسب‌وکار فعلی
فایل‌های انبار/واردات/آواتار/مهر خارج از پشتیبان کسب‌وکار داخل همان آرشیو ZIP
قابلیت انتقال فقط گزارش Excel انتقال کامل با یک فایل به محیط دیگر یا بازنویسی

با این سناریو می‌توان در مراحل بعد دقیقاً روی طراحی کلاس‌ها، نام APIها و تغییرات UI تصمیم گرفت و پیاده‌سازی را شروع کرد.