# EDU-033 — Activate native Promotion combination - **Status:** done — native tenant-owned combination activities, SKU pricing, group records, Trade Order bridge, exact RBAC, corrected Vben UI, and PostgreSQL concurrency evidence delivered - **Type:** native module takeover / special-order promotion / tenant isolation - **Phase:** 5 / commercialization foundation - **Blockers:** EDU-025, EDU-027, PostgreSQL Flyway ## Problem RuoYi already owns the complete group-buying lifecycle: SPU/SKU activity authoring, head/member records, group capacity and expiry, Trade Order integration, app/admin APIs, Member and Product lookups, permissions, and Vben administration. Rebuilding it in Education would create a second promotion/order aggregate and bypass the native Product, Promotion, Trade, Member, tenant, and RBAC boundaries. The source backend has no group-buying capability. Its only `combination` value appears in `docs/pb_schema.json` and `apps/api/src/features/tenant-content/imports.ts`, where it means an education combination-question type. It must not be interpreted as a promotion activity, product, order, or group record. ## Delivered contract - `CombinationActivityDO`, `CombinationProductDO`, and `CombinationRecordDO` extend `TenantBaseDO`; native controllers, services, mappers, Product/Member/Trade APIs, jobs, app endpoints, and Vben pages remain authoritative. - V4450 creates `promotion_combination_activity`, `promotion_combination_product`, and `promotion_combination_record` with `(tenant_id,id)` ownership, PostgreSQL sequences, tenant-qualified Product SPU/SKU/activity/order/head references, snapshot/time/price/limit/state constraints, and fail-closed adoption of pre-existing global tables. - Product triggers require activity/SPU/SKU/status/time consistency and prevent a combination price above the native SKU price. Record and Trade triggers lock the head row, enforce capacity and activity/order/user/head consistency, allow the order-before-record bridge state, and reject deletion of an activity with group records. - Menu IDs 7060–7067 reuse the existing activity and record pages with exactly six controller permissions: five activity permissions and one record query permission. - Request and service validation reject blank/oversized names, invalid time and limit relationships, groups smaller than two, duplicate or mismatched SKUs, non-positive identifiers/prices/counts, prices above the native SKU price, and cross-activity parent groups. Closing an activity transactionally disables product snapshots; activity updates retain and refresh snapshot status/time. - Head creation returns the persisted head-record ID rather than the `0` sentinel. Joining locks the head before validation/insertion, preventing concurrent over-capacity membership. - The Vben activity and record pages use the backend VO shapes and combination-record dictionary, validate positive/time/limit/product inputs, handle missing prices/products, pass the correct head ID to the member dialog, expose exact permission guards, and use viewport-bounded dialogs and keyboard-operable showcase controls. ## Legacy data decision V4450 activates the native tables empty. No legacy rows are imported or invented. Education combination questions remain Education content and are not mapped to Mall group buying; ordinary products and aggregate orders are likewise not reinterpreted. ## Verification - All 52 PostgreSQL Flyway scenarios pass through V4450, including focused table/sequence/constraint/trigger/menu shape, order bridge, cross-tenant same-ID, and fail-closed adoption cases. - A Spring/MyBatis PostgreSQL integration test imports the real native activity and record services plus the production tenant SQL interceptor. Tenants 10 and 20 create the same numeric activity/product IDs; page, record, and summary reads remain isolated; activity edits and closure propagate product snapshots; a head returns its real record ID; two concurrent members competing for the final place produce exactly one success; cross-activity joins and deletion with records fail closed; Trade Order/record/head references remain consistent. - Promotion contract tests prove all three records participate in framework tenant isolation, request DTOs carry the required validation annotations and relationship checks, and the head response conversion never returns the sentinel. - Scoped Vben combination files pass oxlint and oxfmt checks, and the complete `@vben/web-antd` typecheck passes. ## Explicitly open - Bargain, Point, and any other special-order Promotion families require separate tenant-safe activation slices. - Explicit legacy Product/coupon/order/refund/referral/settlement-proof import remains separate; no source combination import is needed. - Purchase-driven Education entitlement fulfillment, refund-driven entitlement revocation, configured Pay runtime, virtual-group expiry job operations, and deployed end-to-end combination checkout evidence remain required for production acceptance.