Skip to content

Type Deep Dive — Campaign Lifecycle And Publishing

1. Bức tranh tổng

Campaign lifecycle của lucky-shaking đang đi qua ba đường khác nhau:

  1. create mới bằng nested insert trực tiếp trên Hasura
  2. update campaign qua action updateGamification
  3. change status qua action changeGamificationStatus

Ngoài ra còn có đường thứ tư là scheduler auto-end campaign hết hạn.

2. Create / Edit Flow ở FE

CampaignCreateUpdate.tsx là shell stepper cho:

  • general info,
  • gifts,
  • UI assets,
  • notifications.

Điểm quan trọng:

  • create và edit dùng chung component,
  • create có thể chọn save draft hoặc publish ngay,
  • edit mode giữ snapshot ban đầu để detect dirty state.

3. Insert Path

Khi tạo mới, FE build mutationObject rồi gọi insert_gamification_one.

Rule đáng chú ý:

  • nếu mode là draft thì set status = gf_status_draft,
  • nếu không, insert thẳng status = gf_status_published (CampaignCreateUpdate.tsx:1058-1061).

Nested create gồm:

  • missions
  • gift_configs
  • files
  • notification_configs

Nghĩa là campaign mới không cần publish action riêng để xuất hiện ở trạng thái phát hành.

4. Update Path

Edit mode gọi action updateGamification(data: ...), không update thẳng Hasura.

Backend rule:

  • chỉ cho update campaign ở draft, published, paused (update_gamification.go:116-123)
  • nếu đã published/paused, backend coi là isPublished = true
  • khi isPublished = true, trường from không được đổi (update_gamification.go:189-205)

Update action còn chia rule nested:

  • missions: soft delete / reopen theo condition
  • gift configs: draft và published có handling khác
  • notification configs: delete old -> insert new
  • files: delete old -> insert new

5. State Machine Action

changeGamificationStatus map action sang target status như sau:

ActionTừ trạng tháiSang trạng thái
action_publishdraftpublished
action_stoppublishedpaused
action_continuepausedpublished
action_endnon-draftended
action_cancellogic check còn lỏngcancelled

FE popup action đang phát action_publish, action_stop, action_continue, action_end, action_cancel trực tiếp (CampaignActionPopup.tsx:31-45).

6. Copy Flow

duplicateGamification:

  • fetch source campaign cùng missions/gift configs/noti/files,
  • tạo ID mới,
  • đặt tên "Bản sao của ..." ,
  • ép status mới về draft,
  • reset time window sang now -> now + 30 ngày,
  • clone toàn bộ nested data (duplicate_gamification.go:124-236).

Copy vì vậy là đường tạo campaign mới từ template sống, không phải snapshot read-only.

7. Scheduler Auto End

end_expired_gamification:

  • query campaign publishedto < now,
  • update status sang ended,
  • đồng thời set to = now (end_expired_gamification.go:28-76).

Điều này có nghĩa:

  • timestamp to ban đầu có thể bị ghi đè khi scheduler end,
  • nếu dùng to vừa như “planned end” vừa như “actual end”, semantics sẽ bị trộn.

8. QA Focus

  1. Tạo campaign draft rồi publish bằng action menu.
  2. Tạo campaign và publish ngay từ create flow, bỏ qua action menu.
  3. Sửa campaign đã published để xác minh from không đổi.
  4. Copy campaign có missions, gifts, notifications và files để xác minh clone đủ nested data.
  5. Pause -> resume -> end -> cancel để xác minh state transitions và guard.
  6. Chạy giả lập scheduler với campaign quá hạn để xác minh auto-end và việc rewrite to.

9. Rủi ro / Findings kỹ thuật

IDMứcFinding
LC-F01P1Publish không tập trung vào một engine; create flow có thể insert thẳng published, nên audit hoặc analytics chỉ nhìn action publish sẽ thiếu case.
LC-F02P1Nhánh cancel trong changeGamificationStatus có comment TODO và check chưa chặt, cho thấy business rule chưa được khóa dứt điểm.
LC-F03P2Scheduler auto-end đang rewrite cả to, nên field này mang nghĩa “actual end” hơn là “configured end” sau khi cron chạy.