03 — WorkflowService — Integration Contract
Integration contract ที่ orchestrator ต้องการจาก Workflow — ไม่ใช่ design ภายใน Workflow; จุด 🤝 = ต้อง confirm กับทีม
อัปเดต: 2026-06-15
อ่าน 00-OVERVIEW.md + CONTEXT.md ก่อน Scope (grill 2026-06-15): ไฟล์นี้ = integration contract ที่ orchestrator ต้องการจาก Workflow — ไม่ใช่ การออกแบบภายใน Workflow; รายละเอียด mapping ภายในเป็นของทีม Workflow ทีมเดียวกัน (ยืนยันแล้ว) แต่ design ภายใน Workflow ยังไม่ชัด → จุดที่มี 🤝 = ต้อง confirm กับทีม
Workflow model จริง (จาก code TakeActionHandler.cs — survey 2026-06-15)
ก่อนคุย contract ต้องเข้าใจว่า Workflow ไม่ได้มี action ตรงกับ vocab ของเรา:
- multi-phase / multi-step:
WfPhaseInstance → WfStepInstance → WfStepActionInstance(SEQUENTIAL/PARALLEL); assignee =UserIdหรือRole - action มีแค่
APPROVE/REJECT— ไม่มี Rework/SentToReviewer/Pickup เป็น action แยก - phase progression: APPROVE step สุดท้ายของ phase → เปิด phase ถัดไป; APPROVE phase สุดท้าย →
COMPLETED - Rework = REJECT +
OnRejectToPhaseOrder>0(ย้อน phase +IsEditable=true);=0→REJECTEDถาวร - webhook มีอยู่แล้ว:
OnActionChangedPayload { StepInstanceId, ActionType, BusinessRefId, ActionBy, NextAssignees, IsEdit, Remark }ส่งผ่านSendWorkflowEventAsync
Mapping ที่ orchestrator ต้องการ (Workflow publisher แปลง native → semantic)
| Orchestrator ต้องการ (M3-M5) | Workflow native | หมายเหตุ 🤝 |
|---|---|---|
| Pickup (→ InReview) | ❓ ไม่มี native | ขึ้นกับ Workflow มี claim/pickup ไหม — ถ้าไม่มี InReview ตัดทิ้ง/ปรับ (open item) |
| SentToReviewer (→ Reviewed) | APPROVE บน phase ที่ไม่ใช่ phase สุดท้าย | maker (phase 1) approve = ส่งต่อ reviewer (phase 2) |
| Decision: Approve | APPROVE บน phase สุดท้าย → COMPLETED | |
| Decision: Reject | REJECT + OnRejectToPhaseOrder=0 | |
| Decision: Rework | REJECT + OnRejectToPhaseOrder>0 (+targets/note) | reworkTargets map จาก step ที่ตีกลับ |
| Decision: Cancel | (ยังไม่มีใน TakeAction) | ต้องเพิ่ม cancel path |
คำถามค้างกับทีม Workflow: (1) มี pickup/claim ไหม → ตัดสิน InReview; (2) แปลง native→semantic ที่ publisher ฝั่ง Workflow (แนะนำ — Workflow รู้ phase semantic ของตัวเอง); (3) mint
decisionId; (4)occurredAtทุก event (Lego ordering ใช้)
งานที่ Workflow ต้องทำ (contract obligations)
W1 — ASB publisher (emit semantic events)
สถานะเดิม: ASB = 0 (WebhookService no-op / webhook HTTP) → ต้องเพิ่ม ASB publisher emit:
| Event (semantic) | ยิงตอน (native) | Payload หลัก |
|---|---|---|
| Pickup (ถ้ามี) | maker claim งาน | flowInstanceId/refNo, makerIdentity, occurredAt |
| SentToReviewer | APPROVE non-final phase | flowInstanceId/refNo, reviewerIdentity (next assignees), occurredAt |
| Decision | APPROVE final / REJECT | flowInstanceId/refNo, action(Approve/Reject/Rework/Cancel), decisionId, reworkTargets, reason, occurredAt |
W2 — Notify maker เอง (grill 2026-06-15 — Workflow เป็นเจ้าของ)
- maker pool → notify ตอนงานเข้ากองกลาง (create instance, unassigned)
- assignee (maker คนเดิม) → notify ตอน resubmit/resume
- orchestrator ไม่ notify maker → Workflow ถือ maker identity เอง ยิงเอง
W3 — mint decisionId + map Rework
- ปัจจุบัน TakeAction รับ APPROVE/REJECT, REJECT+OnRejectToPhaseOrder = rework → publisher ต้อง mint
decisionId+ แปลงเป็น Decision(Rework) พร้อม reworkTargets
W4 — HTTP endpoints ให้ orchestrator เรียก (M12-M14)
| Endpoint | สถานะ | งาน |
|---|---|---|
| create instance (M12) | ⚠️ PostInstances คืน int id | ต้องคืน WfInstanceId ให้ orchestrator เก็บใน Correlations |
| resubmit (M13) | ✅ Resubmit มี | ยืนยัน contract (resume หลัง rework) |
| force-stop (M14) | ✅ ForceStopStep มี | ยืนยัน idempotent (stop ซ้ำ = no-op; ทน “ยังไม่ถูกสร้าง”) |
W5 — Background sweep หมดอายุ
- งาน expired → cancel local + publish Decision(Cancel, mode=Expiry) → orchestrator coordinate teardown
Open items (ต้องตัดสินกับทีม Workflow ก่อน lock contract)
- pickup/claim มีไหม → ตัดสิน
InReview(กระทบ status list ฝั่ง Lego) - แปลง native (APPROVE/REJECT/OnRejectToPhaseOrder) → Decision vocab ที่ publisher
- payload M3-M5 field-level
- reviewer = phase ถัดไป (assignee ของ next phase) — ยืนยัน model
- create คืน WfInstanceId