การคำนวณระดับสมาชิก Loyalty ประจำวัน
ภาพรวม
โปรแกรมสะสมแต้มที่เปิดใช้ระบบระดับสมาชิก เช่น Silver, Gold, Platinum จำเป็นต้องประเมินระดับใหม่เป็นระยะ
เหตุผลที่งานนี้ต้องเป็น cron แทนที่จะทำงานตาม event คือหน้าต่างการวัดเป็นแบบ rolling ลูกค้าอาจถูกลดระดับโดยที่ไม่ได้ทำอะไรเลย เพียงเพราะยอดซื้อเก่าหลุดออกจากช่วง 3 เดือนล่าสุด ไม่มี event ใดจากฝั่งลูกค้าที่จะกระตุ้นการคำนวณนั้นได้ จึงต้องมีตัวที่เดินตรวจตามเวลา
งานนี้ถูกออกแบบให้เป็น เส้นทางเดียวที่สามารถลดระดับสมาชิกได้ ส่วนการเลื่อนระดับขึ้นจะเกิดขึ้นทันที ณ จุดชำระเงินผ่านฟังก์ชันสะสมแต้มฝั่ง client-api เพราะไม่ควรมีลูกค้ารายใดถูกลดระดับกลางการทำรายการ ต่อหน้าพนักงานและคิวลูกค้าคนอื่น
Business Flow
งานทำงานเวลา 03:00 น. ของทุกวัน ตามตาราง 0 3 * * * บน profile cron-scheduler
เวลาดังกล่าวถูกเลือกเพราะอยู่หลังเที่ยงคืน ทำให้ช่วง "3 เดือนล่าสุด" หมายถึงวันเต็ม
และเป็นช่วงที่ระบบเงียบ ซึ่งสำคัญเพราะงานนี้แตะทุกบัญชีบนแพลตฟอร์ม
- ดึงทุกโปรแกรมใน
loyalty.programที่เปิดใช้ระบบ tier และยังไม่ถูกแทนที่ พร้อมค่ากำหนดmode,tier_window_months,tier_fallbackและtier_fallback_tier_id - ประมวลผลทีละโปรแกรม โปรแกรมที่เกิดข้อผิดพลาดจะถูกบันทึก log แล้วข้ามไปตัวถัดไป เพราะการกวาดข้อมูลทั้งแพลตฟอร์มรายคืนต้องไม่หยุดเพียงเพราะร้านเดียวมีปัญหา
- โหลดบันไดระดับสมาชิก ซึ่งประกอบด้วยลำดับขั้น เงื่อนไข ระยะเวลาหน้าต่างของแต่ละขั้น
และค่าระดับเริ่มต้น
- หากโปรแกรมเปิดใช้ระบบ tier แต่ยังไม่ได้นิยามระดับใดเลย ระบบจะไม่ทำอะไรทั้งสิ้น เพราะการลดระดับทุกคนเนื่องจากการตั้งค่ายังไม่เสร็จถือเป็นความเสียหายที่ไม่จำเป็น
- ดึงทุกบัญชีใน
loyalty.accountของ OA นั้นพร้อมระดับปัจจุบัน โดยใช้ LEFT JOIN เพื่อให้บัญชีที่ไม่มียอดในหน้าต่างเลยยังปรากฏด้วยค่า 0 แทนที่จะหายไป เพราะกลุ่มนี้คือกลุ่มที่ควรถูกลดระดับพอดี - คำนวณสถิติแยกตามหน้าต่างของแต่ละระดับ เนื่องจากแต่ละระดับสามารถกำหนดหน้าต่างของตัวเองได้ โดยยิง query หนึ่งครั้งต่อหนึ่งหน้าต่างที่แตกต่างกัน สถิติที่วัดได้แก่ ยอดใช้จ่าย จำนวนคำสั่งซื้อ คะแนนสะสม และจำนวนเดือนที่เป็นสมาชิก
- ประเมินเงื่อนไขของแต่ละระดับ โดยเงื่อนไขประกอบด้วยตัวเชื่อมแบบ and หรือ or และรายการกฎย่อยที่ระบุ metric, operator และค่าที่ใช้เทียบ จากนั้นเลือกระดับสูงสุดที่ผ่านเงื่อนไข
- เทียบผลที่ได้กับระดับปัจจุบันและนโยบาย
tier_fallbackเพื่อตัดสินว่าจะคงระดับเดิม เลื่อนขึ้น หรือลดลงไปที่ระดับใด - เขียนระดับใหม่ลง
loyalty.accountพร้อมบันทึกประวัติการเปลี่ยนระดับ
ไฟล์และฟังก์ชันหลัก
| ไฟล์ | หน้าที่ |
|---|---|
internal/cronscheduler/loyalty_tier.go | LoyaltyTierService.Run เป็น entry point ของ cron โดยมีฟังก์ชันย่อย runProgram, loadLadder, statsForWindow, applyChange, qualifiesForTier, matchTierRule, tierStatFor, pickTierByRules, resolveFromEarned, parseTierConditions และโครงสร้าง tierRow, tierStats, tierRule, tierConditions |
internal/cronscheduler/loyalty_tier_test.go | Unit test ของ logic การเลือกระดับสมาชิก |
cmd/worker/main.go | runCronScheduler — จุดที่ลงทะเบียน job ด้วยตาราง 0 3 * * * |
งานนี้ไม่มีคิวที่เกี่ยวข้อง เป็น cron ล้วน
จุดเชื่อมต่อกับ Service อื่น
- ตารางใน schema
loyalty—program(ค่าtier_enabled,tier_window_months,tier_fallback),tier(บันไดระดับพร้อมเงื่อนไขแบบ jsonb และwindow_months),account(ฟิลด์tier_id) และตารางธุรกรรมที่ใช้คำนวณยอดใช้จ่าย คำสั่งซื้อ และคะแนน - PostgreSQL อย่างเดียว — ไม่ใช้ Redis, RabbitMQ หรือ LINE API
- client-api-go — เป็นผู้เลื่อนระดับขึ้นทันทีตอนสะสมแต้ม ซึ่งเป็นคนละทิศทางกับงานนี้
- cms-api-go — เจ้าของหน้าตั้งค่าบันไดระดับและเงื่อนไขของแต่ละระดับ