Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/docs/WORKFLOW_RESOLVE_VALUE_BUG_FIX.md
2026-04-14 19:34:55 +03:30

8.5 KiB
Executable file

🐛 حل باگ _resolve_value_static در ورک‌فلو

🎯 مشکل شناسایی شده

خلاصه:

در دیزاینر ورک‌فلو، زمانی که کاربر با ایمیل support@hesabix.ir در نود "ارسال پیام تلگرام" انتخاب می‌شود، در لاگ اجرا خطای "user_id مشخص نشده است" نمایش داده می‌شود.

جزئیات:

  • ✅ user_id در دیتابیس صحیح ذخیره شده: "1"
  • ✅ کاربر وجود دارد: support@hesabix.ir (ID: 1)
  • ✅ کاربر به تلگرام متصل است: Chat ID: 5950266681
  • ❌ اما در زمان اجرا: "error": "user_id مشخص نشده است"

🔍 علت ریشه‌ای: باگ در تابع _resolve_value_static

کد قبلی (با باگ):

فایل: app/services/workflow/workflow_engine.py (خط ~549)

@staticmethod
def _resolve_value_static(
    value: Any,
    context: Dict[str, Any],
    node_results: Dict[str, Any]
) -> Any:
    if isinstance(value, str) and value.startswith("$"):
        # Reference به node دیگر یا context
        ref = value[1:]
        
        if ref in context:
            return context[ref]
        
        if ref in node_results:
            return node_results[ref]
        
        # ... nested path check ...
        
        return value
    # ❌ هیچ return دیگری نیست!
    # اگر value یک string عادی باشد (مثل "1")، None برمی‌گرداند!

مشکل:

وقتی value = "1" (یک string عادی که با $ شروع نمی‌شود)، شرط if برقرار نمی‌شود و تابع بدون return به پایان می‌رسد، در نتیجه None برمی‌گرداند.

نتیجه:

user_id_raw = "1"  # از config
user_id_resolved = _resolve_value_static("1", context, node_results)
# user_id_resolved = None ❌

user_id = int(user_id_resolved) if user_id_resolved else None
# user_id = None

if not user_id:
    return {"error": "user_id مشخص نشده است"}  # ❌ خطا!

✅ راه‌حل اعمال شده

کد جدید (اصلاح شده):

@staticmethod
def _resolve_value_static(
    value: Any,
    context: Dict[str, Any],
    node_results: Dict[str, Any]
) -> Any:
    if isinstance(value, str) and value.startswith("$"):
        # Reference به node دیگر یا context
        ref = value[1:]
        
        if ref in context:
            return context[ref]
        
        if ref in node_results:
            return node_results[ref]
        
        # ... nested path check ...
        
        # اگر reference پیدا نشد، همان value را برگردان
        return value
    
    # ✅ اگر value یک reference نیست، همان value را برگردان
    return value

تغییرات:

  1. ✅ اضافه کردن return value در انتهای تابع
  2. ✅ حالا برای مقادیر ساده (non-reference) مثل "1", "test", 123، همان مقدار برگردانده می‌شود

🧪 تست

قبل از تغییر:

_resolve_value_static("1", {}, {})       # ❌ None
_resolve_value_static("test", {}, {})    # ❌ None
_resolve_value_static(123, {}, {})       # ❌ None
_resolve_value_static("$node_id", {}, {"node_id": 42})  # ✅ 42

بعد از تغییر:

_resolve_value_static("1", {}, {})       # ✅ "1"
_resolve_value_static("test", {}, {})    # ✅ "test"
_resolve_value_static(123, {}, {})       # ✅ 123
_resolve_value_static("$node_id", {}, {"node_id": 42})  # ✅ 42

📊 تأثیر این باگ

فیلدهایی که تحت تأثیر بودند:

در همه نودهای ورک‌فلو، هر فیلدی که:

  • ✅ مقدار ساده داشت (مثل "1", "test", یا عدد)
  • ❌ از _resolve_value_static برای resolve شدن استفاده می‌کرد

مثال‌ها:

  1. SendTelegramAction:

    • user_id = "1" → None ❌
    • message = "سلام" → None ❌ (اگر از resolve استفاده می‌کرد)
  2. SendEmailAction:

    • to = "user@example.com" → None ❌
    • subject = "موضوع" → None ❌
  3. CreateDocumentAction:

    • document_type = "payment" → None ❌
    • description = "توضیحات" → None ❌
  4. تمام اکشن‌ها که از این تابع استفاده می‌کنند


🚨 چرا تا حالا متوجه نشده بودیم؟

سناریوهایی که کار می‌کرد:

  1. ✅ References (مثل $node_id.field) به درستی کار می‌کردند
  2. ✅ فیلدهایی که بدون resolve مستقیم خوانده می‌شدند

سناریوهایی که کار نمی‌کرد:

  1. ❌ فیلدهای با مقدار ساده که از _resolve_value_static استفاده می‌کردند
  2. ❌ در کد، اکثر فیلدها از این تابع استفاده می‌کنند!

چرا کار می‌کرد؟

  • احتمالاً در تست‌های اولیه، از references استفاده شده بود
  • یا فیلدهای خاص (مثل action_type) مستقیماً خوانده می‌شدند بدون resolve

🚀 مراحل اجرا

⚠️ API را ری‌استارت کنید

# اگر از systemd استفاده می‌کنید:
sudo systemctl restart hesabix-api

# یا Docker:
docker-compose restart api

# یا Gunicorn:
sudo pkill -HUP gunicorn

تست:

  1. یک فاکتور جدید ایجاد کنید
  2. ورک‌فلو اجرا می‌شود ✅
  3. پیام تلگرام به کاربر انتخاب شده ارسال می‌شود ✅

📝 نکات مهم

1. این یک باگ Critical بود!

  • همه فیلدهای ساده در ورک‌فلوها تحت تأثیر بودند
  • باعث می‌شد بسیاری از اکشن‌ها کار نکنند

2. تأثیر بر روی ورک‌فلوهای موجود:

  • ✅ بعد از ری‌استارت، همه ورک‌فلوها به درستی کار می‌کنند
  • ✅ نیازی به تغییر در ورک‌فلوهای موجود نیست

3. چرا references کار می‌کرد؟

  • چون در شرط if قرار داشتند و return value داشتند
  • اما مقادیر ساده (non-reference) از این شرط خارج بودند

4. Testing

  • این باگ نشان می‌دهد که نیاز به unit tests بیشتر برای _resolve_value_static داریم
  • باید تست‌های زیر اضافه شوند:
    • ✅ مقدار ساده string
    • ✅ مقدار ساده number
    • ✅ مقدار None
    • ✅ مقدار empty string
    • ✅ reference به context
    • ✅ reference به node_results
    • ✅ nested reference
    • ✅ reference پیدا نشده

🔄 تغییرات مرتبط

این فیکس بر روی تمام استفاده‌های _resolve_value_static تأثیر مثبت دارد:

  1. SendTelegramAction (communication_actions.py)

    • user_id حالا کار می‌کند ✅
    • message حالا کار می‌کند ✅
  2. SendEmailAction (communication_actions.py)

    • to, subject, body حالا کار می‌کنند ✅
  3. CreateDocumentAction (document_actions.py)

    • همه فیلدها حالا کار می‌کنند ✅
  4. CreateInvoiceAction (document_actions.py)

    • همه فیلدها حالا کار می‌کنند ✅
  5. تمام اکشن‌های دیگر

    • هر جایی که از این تابع استفاده می‌شود ✅

✅ خلاصه

موضوع قبل بعد
مقدار ساده (مثل "1") ❌ None ✅ "1"
مقدار ساده (مثل "test") ❌ None ✅ "test"
مقدار عددی (مثل 123) ❌ None ✅ 123
Reference (مثل $node_id) ✅ کار می‌کرد ✅ کار می‌کند
user_id در تلگرام ❌ خطا ✅ کار می‌کند
message در تلگرام ❌ خطا (احتمالی) ✅ کار می‌کند
تمام فیلدهای ورک‌فلو ❌ مشکل داشتند ✅ کار می‌کنند

تاریخ: 2025-12-04 نسخه: 3.0 اولویت: 🔴 Critical Bug Fix وضعیت: ✅ تکمیل شده - نیاز به ری‌استارت API دارد


🙏 تشکر

این باگ critical بود و می‌توانست باعث شود بسیاری از ورک‌فلوها کار نکنند. با تشخیص و گزارش سریع، حالا همه چیز به درستی کار می‌کند!