Get a quote

أتمتة سير العمل الداخلي للفرق التشغيلية في الشركات

الأتمتة الموجهة للعملاء تحصل على معظم الاهتمام الهندسي. سير العمل الداخلي هو حيث تُهدَر الساعات الفعلية يومياً. نظام سير عمل مبني بـ Go وPostgreSQL يُتيح لفريق العمليات تعديل منطق الأعمال بدون مطور.

لماذا الأتمتة الداخلية أكثر أثراً من الأتمتة المواجهة للعملاء

معظم شركات التقنية تُركّز الأتمتة في الجانب المواجه للعملاء: نماذج التسجيل، رسائل البريد التحفيزية، تدفقات الدفع. لكن العمليات الداخلية هي حيث تُهدَر الساعات الفعلية.

فريق العمليات في شركة SaaS متوسطة في لبنان أو السعودية قد يقضي ساعتين يومياً في مهام متكررة: مراجعة طلبات جديدة، إرسال تنبيهات للمحاسبة، تحديث حالة الطلبات، توليد تقارير يومية. نظام سير عمل داخلي مبني على Go يُعيد هذه الساعات للإنتاج.

الفرق بين محرك سير العمل ومهمة cron

مهمة cron تُنفّذ دالة ثابتة بجدول زمني. محرك سير العمل يُنفّذ سلسلة قابلة للتهيئة من الخطوات استجابةً لحدث، مع تفريع شرطي، معالجة أخطاء لكل خطوة، سجل تنفيذ كامل، وإمكانية تعديل سير العمل بدون نشر كود جديد.

الفرق الجوهري: حين يُخزَّن تعريف سير العمل في قاعدة البيانات لا في الكود، يستطيع فريق العمليات تعديل منطق الأعمال بدون فتح pull request. المهندسون يبنون مكتبة الإجراءات ومحرك التنفيذ. فريق العمليات يُهيئ ما يعمل ومتى.

مخطط قاعدة البيانات

CREATE TABLE workflow_definitions (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id     UUID NOT NULL,
    name          TEXT NOT NULL,
    trigger_type  TEXT NOT NULL,
    definition    JSONB NOT NULL,
    is_active     BOOLEAN NOT NULL DEFAULT true,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE workflow_runs (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    definition_id   UUID NOT NULL REFERENCES workflow_definitions(id),
    trigger_payload JSONB NOT NULL,
    status          TEXT NOT NULL DEFAULT 'running',
    current_step    INT NOT NULL DEFAULT 0,
    step_results    JSONB NOT NULL DEFAULT '[]',
    started_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

عمود step_results يُسجّل نتيجة كل خطوة مع المدخلات والمخرجات والتوقيت والأخطاء. حين يسأل فريق العمليات "ماذا جرى لهذا الطلب بالضبط؟"، سجل تشغيل سير العمل يُجيب بشكل كامل بدون مطور.

محرك التنفيذ

func (e *WorkflowEngine) Execute(ctx context.Context, runID uuid.UUID) error {
    run, def, err := e.db.LoadRunWithDefinition(ctx, runID)
    if err != nil {
        return err
    }

    for i := run.CurrentStep; i < len(def.Steps); i++ {
        step := def.Steps[i]
        start := time.Now()

        handler, ok := e.actions[step.Action]
        if !ok {
            return fmt.Errorf("إجراء غير معروف: %s", step.Action)
        }

        err := handler(ctx, step.Params, run.TriggerPayload)
        result := StepResult{
            StepName:   step.Name,
            StartedAt:  start,
            DurationMs: time.Since(start).Milliseconds(),
        }

        if err != nil {
            result.Status = "failed"
            result.Error = err.Error()
            if step.OnError == "stop" {
                e.db.RecordStepResult(ctx, runID, result)
                return e.db.MarkRunFailed(ctx, runID, i, err.Error())
            }
            // on_error: continue - استمر رغم الفشل
        } else {
            result.Status = "completed"
        }

        e.db.RecordStepResult(ctx, runID, result)
        e.db.UpdateCurrentStep(ctx, runID, i+1)
    }

    return e.db.MarkRunComplete(ctx, runID)
}

مكتبة إجراءات مفيدة للشركات في المنطقة

// إشعار واتساب عبر WhatsApp Business API
e.RegisterAction("whatsapp_notify", func(ctx context.Context, params, payload map[string]any) error {
    phone := resolveRef(params["phone"], payload)
    template := params["template"].(string)
    vars := resolveRefs(params["template_vars"], payload)
    return whatsappClient.SendTemplate(ctx, phone, template, vars)
})

// تحديث سجل في قاعدة البيانات
e.RegisterAction("update_record", func(ctx context.Context, params, payload map[string]any) error {
    table := params["table"].(string)
    id := resolveRef(params["id"], payload)
    fields := resolveRefs(params["fields"], payload)
    return db.UpdateRecord(ctx, table, id, fields)
})

// توليد تقرير وإرساله بالبريد
e.RegisterAction("send_email", func(ctx context.Context, params, payload map[string]any) error {
    to := toStringSlice(params["to"])
    subject := renderTemplate(params["subject"].(string), payload)
    body := renderTemplate(params["template"].(string), payload)
    return emailClient.Send(ctx, to, subject, body)
})

مثال عملي: معالجة الطلبات الجديدة

تعريف سير عمل يُنفَّذ تلقائياً عند كل طلب جديد:

{
  "name": "معالجة طلب جديد",
  "trigger_type": "new_order",
  "steps": [
    {
      "name": "تأكيد الطلب",
      "action": "update_record",
      "params": {
        "table": "orders",
        "id": "{{trigger.order_id}}",
        "fields": { "status": "confirmed" }
      }
    },
    {
      "name": "إشعار العميل",
      "action": "whatsapp_notify",
      "params": {
        "phone": "{{trigger.customer_phone}}",
        "template": "order_confirmed"
      },
      "on_error": "continue"
    },
    {
      "name": "تنبيه فريق العمليات",
      "action": "send_email",
      "params": {
        "to": ["[email protected]"],
        "subject": "طلب جديد: {{trigger.order_id}}",
        "template": "ops_new_order"
      }
    }
  ]
}

فريق العمليات يستطيع تعديل هذا التعريف من لوحة الإدارة بدون مطور.

تشغيل سير العمل من أحداث التطبيق

func (s *OrderService) CreateOrder(ctx context.Context, req CreateOrderRequest) (*Order, error) {
    order, err := s.db.InsertOrder(ctx, req)
    if err != nil {
        return nil, err
    }

    // غير محجوب: محرك سير العمل يعالج هذا بشكل غير متزامن
    go s.workflowTrigger.Fire(context.Background(), order.TenantID, "new_order", map[string]any{
        "order_id":       order.ID,
        "customer_phone": order.CustomerPhone,
        "total_amount":   order.TotalAmount,
    })

    return order, nil
}

إنشاء الطلب لا ينتظر تنفيذ سير العمل. إذا كان محرك سير العمل غير متاح مؤقتاً، يُعالَج سجل التشغيل عند الاسترداد.

الدروس الأساسية من الإنتاج

خزّن تعريفات سير العمل في قاعدة البيانات لا في الكود. المستفيد الأول هو فريق العمليات. يُتيح لهم تكرار منطق الأعمال بدون شحن كود.

سجّل سجل تنفيذ كامل في كل تشغيل. سجل step_results هو أداة التشخيص الأولى لفريق العمليات.

on_error: continue للخطوات الإخطارية. فشل إرسال رسالة واتساب لا يجب أن يُوقف معالجة الطلب بالكامل.

افصل الحدث المُشغّل عن التنفيذ. كود التطبيق لا يجب أن ينتظر اكتمال سير العمل.


Voxire تبني أنظمة تشغيل داخلية لشركات SaaS والأعمال التشغيلية في لبنان وعبر المنطقة. إذا أردت أتمتة العمليات الداخلية أو بناء منصة تشغيل يستطيع فريقك تهيئتها بدون تدخل هندسي مستمر، تواصل معنا على https://voxire.com/get-a-quote/

العودة إلى المدونة
Chat on WhatsApp