How the Coupon and Voucher System Works in the Mall Project: A Complete Technical Guide
The Mall project implements a full coupon lifecycle across three architectural layers—data entities in mall-mbg, admin management in mall-admin, and member-side operations in mall-portal—supporting global, category-specific, and product-specific discounts with automatic rollback on order cancellation.
The macrozheng/mall repository provides a production-ready e-commerce platform with a robust voucher system that handles everything from creation to redemption. This guide examines the complete technical implementation, referencing actual source files from the MyBatis data layer through to the portal service implementations.
Coupon Data Model and Architecture
The system separates concerns across three distinct layers to manage voucher logic cleanly.
Core Entities and Relationships
The data model centers on four main entities stored in the mall-mbg module:
SmsCoupon– Stores the master definition includingtype(discount type),amount(face value),minPoint(minimum spend),useType(scope restriction), validity periods, and inventory counts (count,receiveCount,publishCount).SmsCouponHistory– Tracks every member claim with fields formemberId,couponId,couponCode,useStatus(0=unused, 1=used), and timestamps.SmsCouponProductRelation– Links coupons to specific product IDs whenuseType = 2.SmsCouponProductCategoryRelation– Links coupons to category IDs whenuseType = 1.
Persistence Layer
MyBatis mappers handle all database interactions:
SmsCouponMapper.xml– CRUD operations for thesms_coupontable.SmsCouponHistoryDao.xml– Complex joins for retrieving detailed coupon histories with product and category data.
Admin Coupon Management
Administrators define voucher rules through the mall-admin service layer before members can claim them.
Creating Coupon Definitions
In mall-admin/src/main/java/com/macro/mall/service/impl/SmsCouponServiceImpl.java, the create method (lines 38-58) processes new vouchers:
- Receives a
SmsCouponParamDTO from the controller. - Sets initial inventory:
countequalspublishCount(total available),receiveCountstarts at 0. - Persists the base
SmsCouponentity. - Inserts relation records into
SmsCouponProductRelationorSmsCouponProductCategoryRelationbased on theuseTypeparameter.
Scope Restrictions and Use Types
The useType field determines voucher applicability:
- 0 (Global) – Usable on any order meeting the minimum spend.
- 1 (Category-specific) – Restricted to products in linked categories via
SmsCouponProductCategoryRelation. - 2 (Product-specific) – Restricted to specific SKUs via
SmsCouponProductRelation.
Member Coupon Issuance and Claiming
The mall-portal module handles member-facing operations through UmsMemberCouponServiceImpl.
The Claim Process
The add(Long couponId) method (lines 43-80) implements a thread-safe claiming workflow:
- Existence Check – Verifies the coupon exists and
count > 0. - Timing Validation – Confirms
enableTimehas passed (claim start date). - Per-User Limit – Queries
SmsCouponHistoryto ensure the member hasn't exceededperLimit. - Inventory Deduction – Decrements
sms_coupon.countand incrementsreceiveCount. - History Creation – Inserts a new
SmsCouponHistoryrecord with a generated 16-digit code anduseStatus = 0.
Coupon Code Generation Logic
The generateCouponCode method (lines 85-99) constructs unique 16-digit codes using:
- Last 8 digits of current timestamp (millisecond precision).
- 4-digit random number (0000-9999).
- Last 4 digits of the
memberId.
This ensures uniqueness while encoding claim metadata directly in the code string.
Retrieving Detailed Histories
For validation purposes, SmsCouponHistoryDao.getDetailList(memberId) performs SQL joins across the coupon, product relation, and category relation tables to return SmsCouponHistoryDetail objects containing full voucher metadata.
Cart-Level Validation and Eligibility
Before checkout, the system filters member coupons against current cart contents.
Filtering Usable Coupons
The listCart(List<CartPromotionItem> cartItems, int type) method (lines 14-34) separates coupons into usable and unusable lists:
type = 1returns the enableList (coupons applicable to current cart).type = 0returns the disableList (claimed but inapplicable coupons).
For each coupon in the member's history, the method evaluates:
- Expiration (
endTime> now). - Minimum spend (
minPoint<= applicable cart total). - Scope restrictions via
useTypeand relation tables.
Scope-Based Calculations
Private helper methods calculate the relevant cart subset for comparison:
calcTotalAmount– Sum of all cart items.calcTotalAmountByproductCategoryId– Sum of items in specific categories (foruseType = 1).calcTotalAmountByProductId– Sum of specific SKUs (foruseType = 2).
Order Creation and Coupon Application
The order service integrates vouchers into the transaction lifecycle.
Prorating Discounts Across Items
In OmsPortalOrderServiceImpl, the handleCouponAmount method (lines 53-66) distributes the coupon value proportionally:
- Identifies applicable order items based on
useType. - Calculates each item's share of the discount using
calcPerCouponAmount(lines 74-80). - Stores the prorated value in
OmsOrderItem.couponAmount. - Reduces the order total accordingly.
Status Management and Rollback
The system maintains coupon state consistency through status updates:
- Usage Commit –
updateCouponStatus(couponId, memberId, 1)(lines 19-31) marks theSmsCouponHistoryrecord as used upon successful order placement. - Cancellation Rollback – In
cancelOrderandcancelTimeOutOrder(lines 40-42), the method invokesupdateCouponStatuswithuseStatus = 0to restore the coupon to unused status when orders expire or are cancelled by the member.
Summary
- The Mall project implements a three-layer coupon architecture separating data entities (
mall-mbg), admin operations (mall-admin), and member services (mall-portal). - Coupon creation in
SmsCouponServiceImpl.createsupports global, category-specific, and product-specific scopes viauseTypeand relation tables. - Member claiming in
UmsMemberCouponServiceImpl.addenforces inventory limits, per-user restrictions, and timing rules while generating unique 16-digit codes. - Cart validation filters claimed coupons against real-time cart totals and scope restrictions before checkout.
- Order integration prorates discounts across applicable items and provides automatic rollback mechanisms when orders are cancelled or timeout.
Frequently Asked Questions
How does the Mall project prevent duplicate coupon claims?
The UmsMemberCouponServiceImpl.add method queries the SmsCouponHistory table to count existing claims for the specific memberId and couponId combination. If the count equals or exceeds the coupon's perLimit field, the service rejects the claim with an error before any database write occurs.
What happens to a coupon when an order is cancelled?
When OmsPortalOrderServiceImpl.cancelOrder or cancelTimeOutOrder executes, it calls updateCouponStatus with useStatus = 0 to revert the corresponding SmsCouponHistory record to unused status. This occurs synchronously during cancellation and also in the delayed message handler for timeout scenarios, ensuring inventory is accurately restored.
How are category-specific and product-specific coupons validated against the cart?
The UmsMemberCouponServiceImpl.listCart method uses the useType field to determine validation logic. For useType = 1 (category), it calls calcTotalAmountByproductCategoryId to sum only items in linked categories. For useType = 2 (product), it uses calcTotalAmountByProductId to sum only specific SKUs. The coupon is valid only if the calculated subtotal meets the minPoint requirement.
What is the format of generated coupon codes in the system?
Coupon codes are 16-digit strings generated in UmsMemberCouponServiceImpl.generateCouponCode by concatenating the last 8 digits of the current millisecond timestamp, a 4-digit random number, and the last 4 digits of the member ID. This format ensures temporal uniqueness while embedding claim metadata for potential debugging or tracing purposes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →