Files
vmall/openspec/changes/add-flash-sales/design.md
T
Chengdong ZhangandCursor 53548196f8 docs(openspec): plan shop coupons, accounts, points, flash sales, and group buying
Capture exclusive promotion composition, empty-group cancel, and mall surface scope so the five marketing changes can be implemented without stacking or lifecycle ambiguity.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-18 17:32:08 +08:00

3.7 KiB
Raw Blame History

Context

The flash-sale page derives sessions, prices, and sell-through from fixed data. The current checkout locks SKU rows and snapshots their converted prices but has no activity inventory or promotion source. A flash sale must be resolved by the server at checkout.

Goals / Non-Goals

Goals:

  • Let shops publish timed sale sessions and SKU-level activity items.
  • Expose live session discovery and authoritative flash pricing.
  • Protect both normal SKU and reserved activity inventory during checkout.

Non-Goals:

  • Traffic queues, bot prevention, platform promotions, stacking coupons onto flash-priced shop orders, waitlists, and automated retry.

Decisions

Session and item are separate, shop-scoped records

flash_sale_sessions stores shop_id, localized label, active window, and enabled state. flash_sale_items references a session and an owned SKU, and stores fixed sale_price_minor, currency, reserved stock, per-customer limit, and sold count. The service rejects items outside their shop, invalid windows, inactive SKUs, overlapping active sale items for the same SKU, and SKUs that already have an overlapping active group-buying activity.

Checkout discovers eligibility from server state

The customer continues using cart checkout; no displayed price is trusted. For each cart SKU, checkout finds a currently active applicable item, converts its fixed price to the requested order currency, applies it only within the items limit, and snapshots final unit price plus nullable flash_sale_item_id on the order item. Standard quantities beyond the limit retain normal price; no cart can receive two activity prices for one SKU.

Two inventories decrement together

Checkout locks SKUs and candidate flash items in deterministic UUID order. Each activity decrement is conditional on sufficient reserved stock, and each normal SKU decrement is conditional on sufficient SKU stock, all in the existing transaction. Pending-payment cancellation restores both inventory sources based on the order-item activity reference.

Mall and merchant UI use the shared contract

Public APIs return sessions and live products with remaining stock/sell-through. Shop-admin manages only its own sessions and items and exposes a flash-sale nav entry. Mall replaces SECKILL_SESSIONS and derived fixtures. Shoppers add the SKU to the existing cart; checkout does not trust the listing price. Payment and order-detail views show the snapshotted unit price. Catalog product-detail keeps SKU prices in this change.

Exclusive composition with coupons and group buying

A SKU MUST NOT be an eligible flash-sale item and an eligible group-buying activity in overlapping windows; shop configuration and checkout both reject that overlap. If any line on a generated shop order applied flash pricing, checkout SHALL reject coupon_by_shop for that shop (409) even when add-shop-coupons is already archived. This order spec restates coupon persistence and pending-payment coupon restore so later archival does not erase them.

Pending payment holds both inventories

Checkout decrements SKU stock and reserved activity stock before payment. Unpaid orders can exhaust displayable stock until the customer cancels or the pending-payment window ends. This change accepts that hostage; it does not add payment pre-authorization or delayed stock decrement.

Risks / Trade-offs

  • A dual-price cart line is more complex than an activity-only purchase path but preserves existing checkout.
  • Concurrent checkout is the critical risk; integration tests must cover competing final stock and customer limit races.
  • Time-window evaluation is request-time SQL/UTC logic; no scheduler is needed to turn displays off.
  • Pending-payment inventory lock is visible as early sell-out relative to paid demand.