19 ก.พ. 2568·อ่าน 3 นาที

สคีมาฐานข้อมูลแผนและสิทธิ์สำหรับการอัปเกรดและแอดออน

สคีมาฐานข้อมูลสำหรับแผนและสิทธิ์ที่รองรับอัปเกรด แอดออน ทดลอง และการเพิกถอนโดยไม่ต้องฝังกฎในโค้ด โดยใช้ตารางและการตรวจสอบที่ชัดเจน

สคีมาฐานข้อมูลแผนและสิทธิ์สำหรับการอัปเกรดและแอดออน

ทำไมแผนและฟีเจอร์ถึงยุ่งเหยิงได้อย่างรวดเร็ว

แผนดูเรียบง่ายในหน้าราคา: Basic, Pro, Enterprise แต่ความยุ่งเริ่มต้นตั้งแต่คุณพยายามเปลี่ยนชื่อนั้นให้เป็นกฎการเข้าถึงจริงในแอปของคุณ

การฝังเงื่อนไขเช็คฟีเจอร์ (เช่น if plan = Pro then allow X) ใช้ได้สำหรับเวอร์ชันแรก แต่แล้วราคาก็เปลี่ยน ฟีเจอร์ย้ายจาก Pro ไป Basic มีแอดออนใหม่ หรือดีลกับฝ่ายขายรวมแพ็กพิเศษ ทันใดนั้นคุณมีเงื่อนไขเดียวกันซ้ำใน API, UI, แอปมือถือ และงานพื้นหลัง คุณแก้ที่หนึ่งแล้วลืมอีกที่ ผู้ใช้สังเกตเห็น

ปัญหาอีกอย่างคือเวลา การสมัครไม่ได้เป็นป้ายคงที่; มันเปลี่ยนกลางรอบ ใครบางคนอัปเกรดวันนี้ ดาวน์เกรดเดือนหน้า หยุดพัก หรือยกเลิกทั้งที่ยังมีเวลาเสียเงินเหลืออยู่ ถ ั้หากฐานข้อมูลของคุณเก็บแค่ “แผนปัจจุบัน” คุณจะสูญเสียไทม์ไลน์และไม่สามารถตอบคำถามพื้นฐานได้ในภายหลัง: เมื่อวันอังคารที่แล้วพวกเขาเข้าถึงอะไรได้บ้าง? ทำไมฝ่ายสนับสนุนถึงอนุมัติการคืนเงิน?

แอดออนทำให้สถานการณ์แย่ขึ้นเพราะมันข้ามแผน แอดออนอาจปลดล็อกที่นั่งเพิ่ม ยกเลิกข้อจำกัด หรือเปิดฟีเจอร์เฉพาะ ผู้คนสามารถซื้อแอดออนได้บนแผนใดก็ได้ เอาออกทีหลัง หรือเก็บไว้หลังดาวน์เกรด ถ้าเงื่อนไขฝังในโค้ด คุณจะจบด้วยกองกรณีพิเศษที่เพิ่มขึ้นเรื่อยๆ

ต่อไปนี้คือสถานการณ์ที่มักจะทำให้การออกแบบแบบง่ายๆ พัง:

  • อัปเกรดกลางรอบ: การเข้าถึงควรเปลี่ยนทันที แต่การคำนวณปรอทาอาจมีเงื่อนไขต่างกัน
  • การดาวน์เกรดที่ตั้งเวลา: การเข้าถึงอาจยังคงสูงกว่าไปจนกว่าจะสิ้นสุดรอบที่ชำระแล้ว
  • Grandfathering: ลูกค้าเก่าบางคนรักษาฟีเจอร์ที่ลูกค้าใหม่ไม่ได้รับ
  • ดีลเฉพาะ: บัญชีหนึ่งได้ Feature A แต่ไม่ได้ Feature B ทั้งที่มีชื่อแผนเดียวกัน
  • ความต้องการตรวจสอบ: ฝ่ายสนับสนุน การเงิน หรือคอมไพลแอนซ์ถามว่า “จริงๆ แล้วเปิดอะไรไว้และเมื่อไหร่?”

เป้าหมายชัดเจน: โมเดลการควบคุมการเข้าถึงที่ยืดหยุ่นซึ่งเปลี่ยนตามราคา โดยไม่ต้องเขียนตรรกะธุรกิจใหม่ทุกครั้ง คุณต้องการที่เดียวที่จะถามว่า 'พวกเขาทำได้ไหม?' และมีบันทึกในฐานข้อมูลที่อธิบายคำตอบ

เมื่อจบบทความนี้ คุณจะได้รูปแบบสคีมาที่คัดลอกได้: แผนและแอดออนกลายเป็นข้อมูลนำเข้า และ entitlements กลายเป็นแหล่งเดียวของความจริงสำหรับการเข้าถึงฟีเจอร์ แนวทางเดียวกันนี้ยังเหมาะกับตัวสร้างแบบ no-code อย่าง AppMaster เพราะคุณเก็บกฎไว้ในข้อมูลและสามารถคิวรีจาก backend, เว็บ และแอปมือถือได้อย่างสม่ำเสมอ

คำสำคัญ: แผน, แอดออน, สิทธิ์, และการเข้าถึง

ปัญหาการสมัครใช้งานหลายอย่างเริ่มจากปัญหาคำศัพท์ ถ้าทุกคนใช้คำเดียวกันหมายถึงสิ่งต่างกัน สคีมาของคุณจะกลายเป็นกรณีพิเศษ

ต่อไปนี้เป็นคำที่ควรแยกกันชัดในสคีมาฐานข้อมูลแผนและสิทธิ์:

  • Plan: แพ็กพื้นฐานที่ใครสักคนได้รับเมื่อสมัคร (เช่น Basic หรือ Pro) โดยปกติแผนจะกำหนดขีดจำกัดพื้นฐานและฟีเจอร์ที่รวมอยู่
  • Add-on: การซื้อเลือกเสริมที่เปลี่ยนพื้นฐาน (เช่น 'ที่นั่งเพิ่ม' หรือ 'รายงานขั้นสูง') แอดออนควรแนบและเอาออกได้โดยไม่ต้องเปลี่ยนแผน
  • Entitlement: ผลลัพธ์สุดท้ายที่คำนวณแล้วว่า 'สิ่งที่พวกเขามีตอนนี้' หลังจากรวมแผน + แอดออน + การยกเว้น นี่คือสิ่งที่แอปของคุณควรถาม
  • Permission (หรือ capability): การกระทำเฉพาะที่คนทำได้ (เช่น 'export data' หรือ 'manage billing') สิทธิ์มักขึ้นกับบทบาทบวกกับ entitlements
  • Access: ผลลัพธ์ในโลกจริงเมื่อแอปบังคับใช้กฎ (หน้าจอแสดงหรือซ่อนฟีเจอร์, เรียก API ได้หรือถูกบล็อก, ข้อจำกัดถูกนำไปใช้)

Feature flags เกี่ยวข้องแต่ต่างกัน โดยปกติ 'feature flag' เป็นสวิตช์ผลิตภัณฑ์ที่คุณควบคุม (rollouts, experiments, ปิดฟีเจอร์ระหว่างเหตุการณ์) ส่วน 'entitlement' เป็นการเข้าถึงเฉพาะลูกค้าตามที่เขาจ่ายหรือได้รับ ใช้ flags เมื่อต้องการเปลี่ยนพฤติกรรมสำหรับกลุ่มโดยไม่แตะการเรียกเก็บเงิน ใช้ entitlements เมื่อต้องการให้การเข้าถึงตรงกับการสมัคร ใบแจ้งหนี้ หรือสัญญา

ขอบเขตเป็นแหล่งความสับสนอีกประการหนึ่ง แยกแนวคิดเหล่านี้ให้ชัด:

  • User: คนหนึ่งคน เหมาะกับบทบาท (admin vs member) และข้อจำกัดส่วนบุคคล
  • Account (customer): หน่วยที่จ่ายเงิน เหมาะกับข้อมูลการเรียกเก็บเงินและความเป็นเจ้าของการสมัคร
  • Workspace (project/team): ที่ที่งานเกิดขึ้น หลายผลิตภัณฑ์ใช้ entitlements ที่นี่ (ที่นั่ง, พื้นที่จัดเก็บ, โมดูลที่เปิดใช้)

เวลาเป็นสิ่งสำคัญเพราะการเข้าถึงเปลี่ยนแปลง ควรทำแบบจำลองโดยตรง:

  • Start and end: Entitlement อาจมีผลเฉพาะในช่วงเวลาหนึ่ง (ทดลอง, โปรโมชั่น, สัญญารายปี)
  • Scheduled change: การอัปเกรดอาจเริ่มตอนนี้; การดาวน์เกรดมักเริ่มที่การต่ออายุครั้งถัดไป
  • Grace and cancelation: อาจอนุญาตการเข้าถึงจำกัดหลังการชำระเงินล้มเหลว แต่ต้องมีวันสิ้นสุดชัดเจน

ตัวอย่าง: บริษัทอยู่ในแผน Pro, เพิ่ม 'Advanced Reporting' กลางเดือน แล้วตั้งเวลาจะดาวน์เกรดเป็น Basic รอบถัดไป แผนเปลี่ยนทีหลัง แอดออนเริ่มตอนนี้ ชั้นสิทธิ์ (entitlement layer) ยังคงเป็นที่เดียวที่ถามว่า: 'วันนี้ workspace นี้ใช้รายงานขั้นสูงได้ไหม?'

สคีมาพื้นฐานสำหรับแผนและฟีเจอร์

สคีมาที่ดีเริ่มจากเล็ก: แยกสิ่งที่คุณขาย (แผนและแอดออน) ออกจากสิ่งที่คนทำได้ (ฟีเจอร์) ถ้ารักษาสองแนวคิดนี้ชัด การอัปเกรดและแอดออนใหม่กลายเป็นการเปลี่ยนข้อมูล ไม่ใช่การเขียนโค้ดใหม่

ชุดตารางหลักที่ใช้งานได้กับผลิตภัณฑ์สมัครส่วนใหญ่มีดังนี้:

  • products: สิ่งที่ขายได้ (Base plan, Team plan, Extra seats add-on, Priority support add-on)
  • plans: ทางเลือก ถ้าต้องการให้แผนเป็นชนิดพิเศษของ product ที่มีฟิลด์เฉพาะ (billing interval, public display order) หลายทีมเก็บแผนไว้ใน products แล้วใช้คอลัมน์ product_type
  • features: แคตาล็อกความสามารถ (API access, max projects, export, SSO, SMS credits)
  • product_features (หรือ plan_features ถ้าจะแยกแผน): ตารางเชื่อมที่ระบุว่าฟีเจอร์ใดมาพร้อมกับผลิตภัณฑ์ไหน โดยปกติจะมีค่าแนบด้วย

ตารางเชื่อมนี้คือที่ที่พลังส่วนใหญ่เกิดขึ้น ฟีเจอร์ไม่ค่อยเป็นแค่เปิด/ปิด แผนอาจประกอบด้วย max_projects = 10 ในขณะที่แอดออนอาจเพิ่ม +5 ดังนั้น product_features ควรรองรับอย่างน้อย:

  • feature_value (ตัวเลข ข้อความ JSON หรือคอลัมน์แยก)
  • value_type (boolean, integer, enum, json)
  • grant_mode (replace vs add) เพื่อให้แอดออนสามารถ 'เพิ่ม 5 ที่นั่ง' แทนการเขียนทับขีดจำกัดพื้นฐาน

จำลองแอดออนเป็น products ด้วย ความแตกต่างเพียงวิธีการซื้อ ผลิตภัณฑ์แผนพื้นฐานเป็น 'หนึ่งครั้งเท่านั้น' ขณะที่แอดออนอาจอนุญาตจำนวน (quantity) แต่ทั้งคู่แม็ปไปยังฟีเจอร์แบบเดียวกัน วิธีนี้หลีกเลี่ยงกรณีพิเศษเช่น 'ถ้าแอดออน X ให้เปิด Y' กระจัดกระจายอยู่ในโค้ด

ฟีเจอร์ควรเป็นข้อมูล ไม่ใช่ค่าคงที่ในโค้ด หากคุณฝังการเช็คฟีเจอร์ไว้ในบริการหลายที่ คุณจะเจอความไม่สอดคล้อง (เว็บบอกว่าใช่ มือถือบอกว่าไม่ Backend ต่างออกไป) เมื่อฟีเจอร์อยู่ในฐานข้อมูล แอปสามารถถามคำถามเดียวและคุณแก้ไขด้วยการแก้แถวข้อมูล

การตั้งชื่อสำคัญกว่าที่คิด ใช้ตัวระบุที่คงที่ซึ่งไม่เปลี่ยน แม้ชื่อการตลาดจะเปลี่ยน:

  • feature_key เช่น max_projects, sso, priority_support
  • product_code เช่น plan_starter_monthly, addon_extra_seats

เก็บป้ายแสดงผลแยกไว้ (feature_name, product_name) ถ้าใช้ AppMaster Data Designer กับ PostgreSQL การปฏิบัติให้ feature_key และ product_code เป็นค่าเอกลักษณ์จะช่วยได้ทันที: คุณสามารถปรับโครงสร้างใหม่ได้อย่างปลอดภัยโดยยังคงความเสถียรของการผสานและการรายงาน

ชั้นสิทธิ์ (entitlement layer): ที่เดียวที่จะถามว่า 'พวกเขาทำได้ไหม?'

ระบบสมัครหลายระบบพังเมื่อ 'สิ่งที่พวกเขาซื้อ' ถูกเก็บในที่หนึ่ง แต่ 'สิ่งที่พวกเขาทำได้' ถูกคำนวณในหลายเส้นทางโค้ด การแก้คือชั้นสิทธิ์: ตารางเดียว (หรือ view) ที่แสดงการเข้าถึงที่มีผลสำหรับ subject ในช่วงเวลาใดช่วงเวลาหนึ่ง

ถ้าคุณต้องการสคีมาที่ทนต่ออัปเกรด ดาวน์เกรด ทดลอง และการให้สิทธิ์ครั้งเดียว ชั้นนี้คือส่วนที่ทำให้ทุกอย่างคาดเดาได้

ตาราง entitlements ที่ใช้งานได้จริง

คิดว่าทุกแถวเป็นคำกล่าวอ้างหนึ่งข้อ: 'subject นี้มีสิทธิ์ฟีเจอร์นี้ด้วยค่ นี้ ตั้งแต่เวลานี้ถึงเวลานั้น มาจากแหล่งนี้' รูปร่างทั่วไปมีดังนี้:

  • subject_type (เช่น ‘account’, ‘user’, ‘org’) และ subject_id
  • feature_id
  • value (ค่าที่มีผลสำหรับฟีเจอร์นั้น)
  • source (มาจากที่ไหน: ‘direct’, ‘plan’, ‘addon’, ‘default’)
  • starts_at และ ends_at (ends_at เป็นค่า null ได้สำหรับการเข้าถึงต่อเนื่อง)

คุณสามารถเก็บค่า value ได้หลายแบบ: คอลัมน์ text/JSON เดียวบวก value_type หรือคอลัมน์แยกเช่น value_bool, value_int, value_text เก็บให้เรียบและคิวรีง่าย

ประเภทค่าที่ครอบคลุมผลิตภัณฑ์ส่วนใหญ่

ฟีเจอร์ไม่ได้เป็นแค่เปิด/ปิด ประเภทค่าต่อไปนี้ครอบคลุมความต้องการการเรียกเก็บเงินและการควบคุมการเข้าถึงส่วนใหญ่:

  • Boolean: เปิด/ปิด (เช่น can_export = true)
  • จำนวนแบบโควตา: ขีดจำกัด (เช่น seats = 10, api_calls = 100000)
  • ระดับชั้น: ลำดับชั้น (เช่น support_tier = 2)
  • สตริง: โหมดหรือรูปแบบ (เช่น data_retention = '90_days')

ลำดับความสำคัญ: วิธีแก้ข้อขัดแย้ง

ข้อขัดแย้งเป็นเรื่องปกติ ผู้ใช้คนหนึ่งอาจอยู่ในแผนที่ให้ 5 ที่นั่ง ซื้อแอดออนเพิ่ม 10 ที่นั่ง และได้รับการมอบสิทธิ์ด้วยมือจากฝ่ายสนับสนุน

ตั้งกฎชัดเจนและยึดไว้ทุกที่:

  1. การให้สิทธิ์โดยตรงมีน้ำหนักมากกว่าแผน
  2. ถัดมาเป็นแอดออน
  3. แล้วค่าเริ่มต้น

วิธีง่ายคือเก็บแถวผู้สมัครทั้งหมด (ได้จากแผน, ได้จากแอดออน, ได้โดยตรง) แล้วคำนวณ 'ผู้ชนะ' สุดท้ายต่อ subject_id + feature_id โดยการเรียงตามลำดับแหล่งที่มา แล้วเรียงโดย starts_at ล่าสุด

นี่คือสถานการณ์ชัดเจน: ลูกค้าดาวน์เกรดวันนี้ แต่จ่ายแอดออนที่มีผลจนสิ้นเดือน ด้วย starts_at/ends_at บน entitlements การดาวน์เกรดมีผลทันทีสำหรับฟีเจอร์จากแผน ในขณะที่แอดออนยังคงมีผลจนถึง ends_at แอปของคุณสามารถตอบว่า 'พวกเขาทำได้ไหม?' ด้วยคิวรีเดียวแทนการเขียนกรณีพิเศษ

การสมัคร, รายการ และการเข้าถึงที่มีขอบเขตเวลา

เปิดฟลูโฟลว์ของผลิตภัณฑ์
ปรับใช้ backend เว็บไซต์ และแอปมือถือจากโปรเจกต์เดียว
เปิดตัวแอป

แคตาล็อกแผนของคุณ (แผน แอดออน ฟีเจอร์) คือ 'สิ่งที่มีให้' การสมัครเป็น 'ใครมีอะไร และเมื่อไหร่' ถ้าคุณเก็บแยกกัน การอัปเกรดและการยกเลิกจะไม่เป็นเรื่องน่ากลัว

รูปแบบปฏิบัติ: หนึ่ง subscription ต่อ account และหลาย subscription_items ภายใต้มัน (หนึ่งสำหรับแผนพื้นฐาน บวกแอดออนหลายรายการ) ในสคีมานี้ คุณจะมีที่ที่สะอาดเพื่อบันทึกการเปลี่ยนแปลงตามเวลาโดยไม่ต้องเขียนทับกฎการเข้าถึง

ตารางหลักสำหรับการสร้างไทม์ไลน์การซื้อ

เก็บให้เรียบด้วยสองตารางที่คิวรีง่าย:

  • subscriptions: id, account_id, status (active, trialing, canceled, past_due), started_at, current_period_start, current_period_end, canceled_at (nullable)
  • subscription_items: id, subscription_id, item_type (plan, addon), plan_id/addon_id, quantity, started_at, ends_at (nullable), source (stripe, manual, promo)

รายละเอียดทั่วไป: เก็บแต่ละ item พร้อมวันที่ของมัน วิธีนี้คุณสามารถให้แอดออนแค่ 30 วัน หรือให้แผนดำเนินจนสิ้นรอบที่ชำระแล้วแม้ว่าลูกค้าจะตั้งค่าหยุดต่ออายุ

แยกการคำนวณปรอทาและการเรียกเก็บเงินออกจากตรรกะการเข้าถึง

ปรอทา ใบแจ้งหนี้ และการลองชำระเป็นปัญหาบัญชี การเข้าถึงเป็นปัญหา entitlements อย่าใช้ความพยายามคิดว่า 'คำนวณการเข้าถึง' จากบรรทัดใบแจ้งหนี้

ให้เหตุการณ์การเรียกเก็บเงินอัปเดตระเบียนการสมัคร (เช่น ขยาย current_period_end, สร้าง subscription_item ใหม่, หรือตั้ง ends_at) แล้วแอปของคุณตอบคำถามการเข้าถึงจากไทม์ไลน์การสมัคร (และต่อมา จากชั้น entitlement) แทนการคำนวณบิล

การตั้งการเปลี่ยนแปลงตามเวลาโดยไม่เกิดความประหลาดใจ

อัปเกรดและดาวน์เกรดมักมีผลในช่วงเวลาที่ชัดเจน:

  • เพิ่ม pending_plan_id และ change_at ใน subscriptions สำหรับการเปลี่ยนแปลงแผนที่ตั้งค่าได้หนึ่งรายการ
  • หรือใช้ตาราง subscription_changes (subscription_id, effective_at, from_plan_id, to_plan_id, reason) ถ้าต้องการเก็บประวัติและหลายการเปลี่ยนในอนาคต

วิธีนี้ป้องกันการฝังกฎแบบ 'ดาวน์เกรดเกิดขึ้นที่สิ้นรอบ' ในส่วนต่างๆ ของโค้ด ตารางข้อมูลทำหน้าที่เป็นแหล่งความจริง

การทดลอง (trials) อยู่ตรงไหน

Trial เป็นเพียงการเข้าถึงที่มีขอบเขตเวลาโดยมีแหล่งที่มาแตกต่างกัน สองทางเลือกที่ชัด:

  • ถือ trial เป็นสถานะของ subscription (trialing) พร้อมกับ trial_start/trial_end
  • หรือสร้าง subscription_items/entitlements สำหรับทดสอบโดยมี started_at/ends_at และ source = trial

ถ้าสร้างใน AppMaster ตารางเหล่านี้จับคู่ได้ดีใน Data Designer (PostgreSQL) และวันที่ทำให้คิวรี 'อะไรที่ active ตอนนี้' ง่ายโดยไม่ต้องกรณีพิเศษ

ขั้นตอนทีละขั้น: นำรูปแบบไปใช้งาน

สร้างสิทธิ์เป็นข้อมูล
ออกแบบแผน ผลิตภัณฑ์เสริม และสิทธิ์ใน PostgreSQL ด้วยเครื่องมือเชิงภาพ แทนการฝังเงื่อนไขในโค้ด
ลองใช้ AppMaster

สัญญาหนึ่งข้อของสคีมาที่ดีคือ: ตรรกะฟีเจอร์อยู่ในข้อมูล ไม่กระจัดกระจายทั่วโค้ด แอปของคุณควรถามคำถามเดียว — 'สิทธิ์ที่มีผลตอนนี้คืออะไร?' — แล้วได้คำตอบชัดเจน

1) กำหนดฟีเจอร์ด้วยคีย์ที่คงที่

สร้างตาราง feature ด้วยคีย์ที่อ่านง่ายและจะไม่เปลี่ยนชื่อ (แม้ป้าย UI จะเปลี่ยน) คีย์ดีๆ เช่น export_csv, api_calls_per_month, หรือ seats

เพิ่มชนิดเพื่อให้ระบบรู้ว่าจะจัดการค่าอย่างไร: boolean (เปิด/ปิด) vs numeric (ขีดจำกัด) เก็บให้เรียบและสอดคล้อง

2) แม็ปแผนและแอดออนไปยังสิทธิ์

ตอนนี้คุณต้องมีแหล่งความจริงสองแห่ง: สิ่งที่แผนรวม และสิ่งที่แอดออนให้

ลำดับปฏิบัติที่เรียบง่าย:

  • ใส่ฟีเจอร์ทั้งหมดในตาราง feature เดียวด้วยคีย์คงที่และชนิดค่าของมัน
  • สร้าง plan และ plan_entitlement ที่แต่ละแถวให้ค่าฟีเจอร์ (เช่น seats = 5, export_csv = true)
  • สร้าง addon และ addon_entitlement ที่ให้ค่าเพิ่ม (เช่น seats + 10, api_calls_per_month + 50000, หรือ priority_support = true)
  • ตัดสินใจวิธีรวมค่า: boolean มักใช้ OR, ขีดจำกัดเชิงตัวเลขมักใช้ MAX (ค่าสูงสุดชนะ), และจำนวนที่นั่งมักใช้ SUM
  • บันทึกเมื่อ entitlements เริ่มและหมดอายุ เพื่อให้อัปเกรด ยกเลิก และปรอทาไม่ทำให้การเช็คการเข้าถึงพัง

ถ้าสร้างใน AppMaster คุณสามารถโมเดลตารางเหล่านี้ใน Data Designer และเก็บกฎการรวมเป็นตารางนโยบายเล็กๆ หรือ enum ที่ Business Process ใช้

3) ผลิต 'สิทธิ์ที่มีผล'

คุณมีสองตัวเลือก: คำนวณเมื่ออ่าน (query และรวมทุกครั้ง) หรือสร้าง snapshot เก็บแคชเมื่อมีการเปลี่ยนแปลง (แผนเปลี่ยน, ซื้อแอดออน, ต่ออายุ, ยกเลิก) สำหรับแอปส่วนใหญ่ snapshot ง่ายต่อการเข้าใจและเร็วภายใต้โหลด

แนวทางทั่วไปคือมีตาราง account_entitlement ที่เก็บผลลัพธ์สุดท้ายต่อฟีเจอร์ พร้อม valid_from และ valid_to

4) บังคับใช้การเข้าถึงด้วยการเช็คเดียว

อย่ากระจายกฎในหน้าจอ จุดสิ้นสุด และงานพื้นหลัง ใส่ฟังก์ชันเดียวในโค้ดแอปที่อ่านสิทธิ์ที่มีผลและตัดสินใจ

can(account_id, feature_key, needed_value=1):
  ent = get_effective_entitlement(account_id, feature_key, now)
  if ent.type == "bool": return ent.value == true
  if ent.type == "number": return ent.value >= needed_value

เมื่อทุกส่วนเรียก can(...) การอัปเกรดและแอดออนกลายเป็นการอัปเดตข้อมูล ไม่ใช่การเขียนโค้ดใหม่

ตัวอย่างสถานการณ์: อัปเกรดบวกแอดออนโดยไม่มีความประหลาดใจ

ทีมซัพพอร์ต 6 คนอยู่ในแผน Starter ซึ่งรวมที่นั่ง 3 คนและ 1,000 SMS ต่อเดือน กลางเดือนพวกเขาขยายเป็น 6 คนและต้องการแพ็ก SMS เพิ่ม 5,000 ข้อความ คุณต้องการให้ระบบทำงานโดยไม่ต้องโค้ดกรณีพิเศษ เช่น 'ถ้าแผน = Pro แล้ว...'

วันที่ 1: เริ่มใน Starter

สร้าง subscription ให้บัญชีโดยมีรอบการเรียกเก็บ (เช่น 1 ม.ค. ถึง 31 ม.ค.) แล้วเพิ่ม subscription_item สำหรับแผนนั้น

ที่เช็คเอาต์ (หรือผ่านงานกลางคืน) เขียนการให้สิทธิ์สำหรับรอบนั้น:

  • entitlement_grant: agent_seats, ค่า 3, เริ่ม 1 ม.ค., สิ้นสุด 31 ม.ค.
  • entitlement_grant: sms_messages, ค่า 1000, เริ่ม 1 ม.ค., สิ้นสุด 31 ม.ค.

แอปของคุณไม่ถามว่า 'พวกเขาอยู่แผนอะไร?' มันถามว่า 'สิทธิ์ที่มีผลตอนนี้คืออะไร?' และได้ผลลัพธ์ seats = 3, SMS = 1000

วันที่ 15: อัปเกรดเป็น Pro และเพิ่มแพ็ก SMS

วันที่ 15 ม.ค. พวกเขาอัปเกรดเป็น Pro (มีที่นั่ง 10 คนและ 2,000 SMS) คุณไม่แก้ไขการให้สิทธิ์เก่า แต่เพิ่มระเบียนใหม่:

  • ปิดรายการแผนเก่า: ตั้ง subscription_item (Starter) ให้สิ้นสุดที่ 15 ม.ค.
  • สร้างรายการแผนใหม่: subscription_item (Pro) เริ่ม 15 ม.ค., สิ้นสุด 31 ม.ค.
  • เพิ่มรายการแอดออน: subscription_item (SMS Pack 5000) เริ่ม 15 ม.ค., สิ้นสุด 31 ม.ค.

แล้วการให้สิทธิ์ในรอบเดียวกันจะถูกต่อท้าย:

  • entitlement_grant: agent_seats, ค่า 10, เริ่ม 15 ม.ค., สิ้นสุด 31 ม.ค.
  • entitlement_grant: sms_messages, ค่า 2000, เริ่ม 15 ม.ค., สิ้นสุด 31 ม.ค.
  • entitlement_grant: sms_messages, ค่า 5000, เริ่ม 15 ม.ค., สิ้นสุด 31 ม.ค.

ผลทันทีในวันที่ 15:

  • ที่นั่ง: ที่นั่งที่มีผลเป็น 10 (ถ้ากฎคือ 'เอาค่าสูงสุดสำหรับที่นั่ง') พวกเขาสามารถเพิ่มอีก 3 ตัวในวันนั้น
  • SMS: ข้อความที่มีผลเป็น 7,000 สำหรับที่เหลือของรอบ (ถ้ากฎคือ 'รวมแพ็กข้อความแบบเพิ่ม')

ไม่ต้องย้ายการใช้งานเดิม ตารางการใช้งานยังคงนับข้อความที่ส่ง; การเช็คสิทธิ์เพียงเทียบการใช้งานในรอบกับขีดจำกัดที่มีผลตอนนี้

วันที่ 25: ตั้งเวลาดาวน์เกรด ให้เข้าถึงต่อจนสิ้นรอบ

วันที่ 25 ม.ค. พวกเขาตั้งเวลาจะดาวน์เกรดกลับเป็น Starter เริ่ม 1 ก.พ. คุณไม่แตะการให้สิทธิ์ของเดือนมกราคม คุณสร้างรายการในอนาคตสำหรับรอบถัดไป:

  • subscription_item (Starter) เริ่ม 1 ก.พ., สิ้นสุด 28 ก.พ.
  • ไม่มีรายการแพ็ก SMS เริ่ม 1 ก.พ.

ผลลัพธ์: พวกเขายังคงใช้ที่นั่งแบบ Pro และแพ็ก SMS ถึง 31 ม.ค. ในวันที่ 1 ก.พ. ที่นั่งจะลดเป็น 3 และ SMS จะกลับสู่ขีดจำกัดของ Starter สำหรับรอบใหม่ นี่เป็นสิ่งที่คาดเดาได้และจับคู่กับเวิร์กโฟลว์ no-code ใน AppMaster ได้อย่างสะอาด: การเปลี่ยนวันที่สร้างแถวใหม่ และคิวรี entitlement ยังคงเหมือนเดิม

ข้อผิดพลาดและกับดักทั่วไป

คำนวณสิทธิ์ที่ใช้งานจริง
สร้าง snapshot ของสิทธิ์โดยอัตโนมัติเมื่อแผนหรือแอดออนมีการเปลี่ยนแปลง
อัตโนมัติการเข้าถึง

บั๊กการสมัครส่วนใหญ่ไม่ใช่บั๊กบิล แต่เป็นบั๊กการเข้าถึงที่เกิดจากตรรกะกระจัดกระจาย จุดที่ทำให้สคีมาพังเร็วที่สุดคือการตอบ 'พวกเขาทำได้ไหม?' ในหลายที่

ความล้มเหลวคลาสสิกคือการฝังกฎใน UI, API, และงานพื้นหลังแยกกัน UI ซ่อนปุ่ม API ลืมบล็อก endpoint และงานกลางคืนยังทำงานเพราะมันเช็คสิ่งอื่น คุณจะได้รายงาน 'มันทำงานบางครั้ง' ที่แก้ไขยาก

กับดักอีกอย่างคือการใช้ plan_id เช็คแทนการเช็คฟีเจอร์ มันดูเรียบง่าย แต่จะพังเมื่อคุณเพิ่มแอดออน ลูกค้าที่ได้รับสิทธิ์ก่อนหน้านี้ (grandfathered), ทดลองฟรี, หรือข้อยกเว้นขององค์กร ถ้าคุณเคยพูดว่า 'if plan is Pro then allow...' คุณกำลังก่อเขาวงกตที่จะต้องดูแลตลอดไป

ขอบเขตเวลาและการยกเลิกที่ยุ่ง

การเข้าถึงติดอยู่เมื่อคุณเก็บแค่ boolean เช่น has_export = true โดยไม่แนบวันที่ การยกเลิก การคืนเงิน และการดาวน์เกรดกลางรอบต้องการช่วงเวลา หากไม่มี starts_at และ ends_at คุณอาจให้การเข้าถึงถาวรโดยไม่ได้ตั้งใจ หรือเอาการเข้าถึงออกเร็วเกินไป

ข้อควรเช็คง่ายๆ เพื่อหลีกเลี่ยง:

  • ทุกการให้สิทธิ์ต้องมีแหล่งที่มา (plan, add-on, manual override) และช่วงเวลา
  • ทุกการตัดสินใจการเข้าถึงควรใช้ 'ตอนนี้อยู่ระหว่าง start และ end' (มีข้อกำหนดชัดเจนสำหรับ end ที่เป็น null)
  • งานพื้นหลังควรตรวจสอบ entitlements ตอนรันไทม์ ไม่สมมติสถานะของเมื่อวาน

อย่าปนการเรียกเก็บเงินกับการเข้าถึง

ทีมมักมีปัญหาเมื่อผสมเรคคอร์ดการเรียกเก็บเงินกับกฎการเข้าถึงในตารางเดียว การเรียกเก็บเงินต้องการใบแจ้งหนี้ ภาษี การคำนวณปรอทา ไอดีผู้ให้บริการ และสถานะการลองชำระ ขณะที่การเข้าถึงต้องการคีย์ฟีเจอร์ชัดเจนและช่วงเวลา เมื่อทั้งสองพันกัน การย้ายนโยบายบัญชีอาจกลายเป็นการล่มของผลิตภัณฑ์

สุดท้าย หลายระบบข้ามประวัติการตรวจสอบ เมื่อผู้ใช้ถามว่า 'ทำไมฉันส่งออกได้?' คุณต้องตอบได้ว่า: 'เปิดโดย Add-on X ตั้งแต่ 2026-01-01 ถึง 2026-02-01' หรือ 'ให้โดยฝ่ายสนับสนุน ตั๋ว 1842' หากไม่มีสิ่งนั้น ฝ่ายสนับสนุนและวิศวกรจะต้องเดา

ถ้าสร้างใน AppMaster เก็บฟิลด์ตรวจสอบใน Data Designer ของคุณ และทำให้การเช็ค 'พวกเขาทำได้ไหม?' เป็น Business Process เดียวที่เว็บ มือถือ และงานที่ตั้งเวลาทุกตัวใช้

เช็คลิสต์ด่วนก่อนปล่อย

ก้าวข้ามหน้าราคา
ได้โค้ดที่พร้อมใช้งานจากแบ็คเอนด์แบบ no-code และ UI ของคุณ
สร้างโค้ด

ก่อนปล่อยสคีมาฐานข้อมูลแผนและสิทธิ์ ให้ตรวจทานด้วยคำถามจริง ไม่ใช่ทฤษฎี เป้าหมายคือการเข้าถึงต้องอธิบายได้ ทดสอบได้ และเปลี่ยนแปลงง่าย

คำถามตรวจสอบความจริง

เลือกผู้ใช้หนึ่งคนและฟีเจอร์หนึ่งตัว แล้วลองอธิบายผลลัพธ์เหมือนที่อธิบายให้ฝ่ายสนับสนุนหรือการเงิน ถ้าคุณตอบได้แค่ 'พวกเขาอยู่ใน Pro' (หรือแย่กว่านั้น 'โค้ดบอกอย่างนั้น') คุณจะเจอปัญหาเมื่อลูกค้าอัปเกรดกลางรอบหรือได้ดีลพิเศษ

ใช้เช็คลิสต์นี้:

  • คุณตอบได้ไหมว่า 'ทำไมผู้ใช้คนนี้ถึงมีการเข้าถึง?' โดยดูจากข้อมูลเพียงอย่างเดียว (subscription items, add-ons, overrides, และช่วงเวลา) โดยไม่ต้องอ่านโค้ด
  • การเช็คการเข้าถึงทั้งหมดอิงคีย์ฟีเจอร์ที่คงที่ (เช่น feature.export_csv) แทนชื่อแผน (เช่น 'Starter' หรือ 'Business') ชื่อแผนอาจเปลี่ยน; คีย์ฟีเจอร์ไม่ควรเปลี่ยน
  • Entitlements มีเวลาเริ่มและสิ้นสุดชัดเจน รวมทดลอง ช่วงเวลาผ่อนปรน และการยกเลิกที่ตั้งเวลา ถ้าขาดเวลา การดาวน์เกรดจะกลายเป็นข้อถกเถียง
  • คุณให้หรือยกเลิกการเข้าถึงสำหรับลูกค้าหนึ่งรายด้วยระเบียน override ได้ไหม โดยไม่ต้องเปลี่ยนตรรกะ นี่คือวิธีจัดการ 'ให้ที่นั่งเพิ่ม 10 ตัวในเดือนนี้' โดยไม่ต้องโค้ดเฉพาะ
  • คุณทดสอบการอัปเกรดและดาวน์เกรดด้วยตัวอย่างเพียงไม่กี่แถวแล้วได้ผลลัพธ์ที่คาดเดาได้หรือไม่ ถ้าต้องใช้สคริปต์ซับซ้อนเพื่อจำลอง แสดงว่าสมรรถภาพของโมเดลคุณคลุมเครือเกินไป

การทดสอบปฏิบัติ: สร้างผู้ใช้สามคน (ใหม่, อัปเกรดกลางเดือน, ยกเลิก) และแอดออนหนึ่งตัว (เช่น 'ที่นั่งเพิ่ม' หรือ 'รายงานขั้นสูง') แล้วรันคิวรีการเข้าถึงสำหรับแต่ละคน ถ้าผลลัพธ์ชัดเจนและอธิบายได้ คุณพร้อมแล้ว

ถ้าคุณใช้เครื่องมืออย่าง AppMaster ให้ยึดกฎเดียวกัน: ทำให้มีที่เดียว (คิวรีหรือ Business Process เดียว) รับผิดชอบคำถาม 'พวกเขาทำได้ไหม?' ให้ทุกหน้าจอเว็บและมือถือใช้คำตอบเดียวกัน

ขั้นตอนถัดไป: ทำให้อัปเกรดดูแลได้ง่าย

วิธีที่ดีที่สุดในการทำให้อัปเกรดไม่ยุ่งคือเริ่มเล็กกว่าที่คิด เลือกฟีเจอร์ที่ขับเคลื่อนมูลค่าจริงไม่กี่ตัว (5-10 ก็พอ) แล้วสร้างการเช็คสิทธิ์หนึ่งตัวที่ตอบคำถามเดียว: 'บัญชีนี้ทำ X ได้ตอนนี้ไหม?' ถ้าคุณตอบไม่ได้ในที่เดียว การอัปเกรดจะเป็นเรื่องเสี่ยงเสมอ

เมื่อการเช็คเดียวทำงาน ให้มองเส้นทางอัปเกรดเป็นพฤติกรรมผลิตภัณฑ์ ไม่ใช่แค่พฤติกรรมการเรียกเก็บเงิน วิธีที่เร็วที่สุดจะจับกรณีขอบได้คือเขียนชุดทดสอบการเข้าถึงเล็กๆ ตามการเคลื่อนไหวของลูกค้าจริง

ขั้นตอนปฏิบัติที่ให้ผลทันที:

  • กำหนดแคตาล็อกฟีเจอร์ขั้นต่ำและแม็ปแต่ละแผนให้เป็นชุด entitlement ชัดเจน
  • เพิ่มแอดออนเป็น 'รายการ' แยกต่างหากที่ให้หรือขยาย entitlements แทนการฝังไว้ในกฎแผน
  • เขียน 5-10 การทดสอบการเข้าถึงสำหรับเส้นทางทั่วไป (อัปเกรดกลางรอบ, ดาวน์เกรดเมื่อต่ออายุ, เพิ่มแล้วเอาแอดออนออก, ทดลองไปจ่ายเงิน, ช่วงผ่อนปรน)
  • ทำให้การเปลี่ยนแปลงราคาเป็นเรื่องข้อมูล: อัปเดตแถวแผน การแม็ปฟีเจอร์ และการให้สิทธิ์ ไม่ใช่โค้ดแอป
  • ตั้งนิสัย: ทุกแผนหรือแอดออนใหม่ต้องมาพร้อมอย่างน้อยหนึ่งการทดสอบที่พิสูจน์ว่าการเข้าถึงทำงานตามคาด

ถ้าคุณใช้ backend แบบ no-code คุณยังสามารถโมเดลรูปแบบนี้ได้สะอาด ใน AppMaster Data Designer เหมาะสำหรับสร้างตารางหลัก (plans, features, subscriptions, subscription items, entitlements) แล้ว Business Process Editor เก็บโฟลว์การตัดสินใจการเข้าถึง (โหลด entitlements ที่ active, ใช้ช่วงเวลา, คืนค่า allow/deny) ทำให้คุณไม่ต้องเขียนตรรกะกระจัดกระจายใน endpoint

ผลตอบแทนจะปรากฏเมื่อมีการเปลี่ยนแปลงราคา แทนที่จะเขียนกฎใหม่ คุณแก้ข้อมูล: ฟีเจอร์ย้ายจาก 'Pro' เป็นแอดออน ระยะเวลาสิทธิ์เปลี่ยน หรือแผนเก่ารักษาสิทธิ์เดิม ตรรกะการเข้าถึงของคุณคงที่ และการอัปเกรดเป็นการอัปเดตที่ควบคุมได้ ไม่ใช่ sprint โค้ด

ถ้าต้องการตรวจสอบสคีมาอย่างรวดเร็ว ลองโมเดลการอัปเกรดหนึ่งครั้งบวกแอดออนหนึ่งชิ้นตั้งแต่ต้นจนจบ แล้วรันการทดสอบการเข้าถึงก่อนเพิ่มอย่างอื่น

ง่ายต่อการเริ่มต้น
สร้างบางสิ่งที่ น่าทึ่ง

ทดลองกับ AppMaster ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม