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 แอปของคุณสามารถตอบว่า 'พวกเขาทำได้ไหม?' ด้วยคิวรีเดียวแทนการเขียนกรณีพิเศษ

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

เป็นเจ้าของตัวเลือกการปรับใช้ของคุณ
ปรับใช้บนคลาวด์ของคุณหรือส่งออกซอร์สโค้ดเมื่อคุณต้องการควบคุมเต็มที่
ส่งออกซอร์ส

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

รูปแบบปฏิบัติ: หนึ่ง 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 ตอนนี้' ง่ายโดยไม่ต้องกรณีพิเศษ

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

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

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

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 ยังคงเหมือนเดิม

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

เปลี่ยนสคีมาเป็นตาราง
ออกแบบตารางสคีมาจากบทความนี้ใน AppMaster Data Designer ภายในไม่กี่นาที
สร้างแบบจำลองข้อมูล

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

ความล้มเหลวคลาสสิกคือการฝังกฎใน 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 เดียวที่เว็บ มือถือ และงานที่ตั้งเวลาทุกตัวใช้

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

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

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

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

เลือกผู้ใช้หนึ่งคนและฟีเจอร์หนึ่งตัว แล้วลองอธิบายผลลัพธ์เหมือนที่อธิบายให้ฝ่ายสนับสนุนหรือการเงิน ถ้าคุณตอบได้แค่ 'พวกเขาอยู่ใน 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 ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม