How the Flash Sale and Promotion System Works in macrozheng/mall: A Technical Deep Dive
The flash sale and promotion system implements a three-tier hierarchy of Promotion → Session → Product Relation, enabling time-bound discounts through administrative CRUD APIs and real-time portal rendering via HomeServiceImpl#getHomeFlashPromotion().
The flash sale and promotion system in the macrozheng/mall repository provides a complete infrastructure for time-sensitive discount campaigns. Built on a MySQL-backed layered architecture, it separates administrative configuration from customer-facing presentation, allowing merchants to schedule precise inventory-limited flash sales across specific time windows.
Three-Tier Architecture Overview
The system organizes flash sales into three distinct layers, each managed through dedicated models, services, and controllers in the mall-admin module.
Promotion Definition Layer
The top-level SmsFlashPromotion entity defines the overall campaign with date boundaries and status controls. Stored in mall-mbg/src/main/java/com/macro/mall/model/SmsFlashPromotion.java, this model tracks startDate, endDate, and an status flag (1 for enabled, 0 for disabled). The SmsFlashPromotionServiceImpl handles business logic, while SmsFlashPromotionController exposes REST endpoints under /flash.
Promotion Session Layer
Within each promotion, SmsFlashPromotionSession defines daily time windows (e.g., 10:00–12:00). Located in mall-mbg/src/main/java/com/macro/mall/model/SmsFlashPromotionSession.java, sessions store startTime, endTime, and independent status flags. This allows granular control over specific time slots without modifying the parent promotion’s date range.
Product-Session Relation Layer
The SmsFlashPromotionProductRelation entity links actual products to specific promotion sessions. Defined in mall-mbg/src/main/java/com/macro/mall/model/SmsFlashPromotionProductRelation.java, it stores flashPromotionPrice, flashPromotionCount (total stock), and flashPromotionLimit (per-user purchase limits). This bridge table connects sms_flash_promotion, sms_flash_promotion_session, and product catalogs.
Admin Workflow and API Endpoints
Administrators configure flash sales through sequential REST API calls managed by the mall-admin module.
Creating Flash Promotions
To initiate a campaign, admins POST to /flash/create, handled by SmsFlashPromotionController#create:
SmsFlashPromotion flash = new SmsFlashPromotion();
flash.setTitle("双11限时购");
flash.setStartDate(DateUtil.parse("2024-11-11"));
flash.setEndDate(DateUtil.parse("2024-11-12"));
flash.setStatus(1);
int count = flashPromotionService.create(flash);
The service layer sets createTime and persists via SmsFlashPromotionMapper.
Managing Time Sessions
After creating a promotion, admins define time windows via /flashSession/create. The SmsFlashPromotionSessionServiceImpl processes these requests:
SmsFlashPromotionSession session = new SmsFlashPromotionSession();
session.setTitle("上午场");
session.setStartTime(DateUtil.parseTime("10:00:00"));
session.setEndTime(DateUtil.parseTime("12:00:00"));
session.setStatus(1);
flashPromotionSessionService.create(session);
Linking Products to Sessions
Finally, products are associated through /flashProductRelation/create in SmsFlashPromotionProductRelationController:
SmsFlashPromotionProductRelation rel = new SmsFlashPromotionProductRelation();
rel.setFlashPromotionId(flashId);
rel.setFlashPromotionSessionId(sessionId);
rel.setProductId(productId);
rel.setFlashPromotionPrice(new BigDecimal("99.99"));
rel.setFlashPromotionCount(200);
rel.setFlashPromotionLimit(2);
relationService.create(Collections.singletonList(rel));
Status management occurs through /update/status/{id} endpoints that trigger updateByPrimaryKeySelective on the respective mapper.
Portal Implementation: Rendering Active Flash Sales
The mall-portal module renders current flash sales through HomeServiceImpl#getHomeFlashPromotion(), which orchestrates the three-tier hierarchy into a customer-facing display.
Query Logic in HomeServiceImpl
The core method chains temporal queries to identify active promotions:
private HomeFlashPromotion getHomeFlashPromotion() {
HomeFlashPromotion homeFlashPromotion = new HomeFlashPromotion();
Date now = new Date();
// ① Find promotion active on today's date
SmsFlashPromotion flashPromotion = getFlashPromotion(now);
if (flashPromotion != null) {
// ② Find session covering current time
SmsFlashPromotionSession flashPromotionSession = getFlashPromotionSession(now);
if (flashPromotionSession != null) {
homeFlashPromotion.setStartTime(flashPromotionSession.getStartTime());
homeFlashPromotion.setEndTime(flashPromotionSession.getEndTime());
// ③ Fetch next session for "即将开始" display
SmsFlashPromotionSession nextSession = getNextFlashPromotionSession(
homeFlashPromotion.getStartTime());
if (nextSession != null) {
homeFlashPromotion.setNextStartTime(nextSession.getStartTime());
homeFlashPromotion.setNextEndTime(nextSession.getEndTime());
}
// ④ Load products for this promotion-session
List<FlashPromotionProduct> flashProductList =
homeDao.getFlashProductList(
flashPromotion.getId(),
flashPromotionSession.getId());
homeFlashPromotion.setProductList(flashProductList);
}
}
return homeFlashPromotion;
}
Temporal Query Implementation
getFlashPromotion(Date): QueriesSmsFlashPromotionwherestatus = 1andnowfalls betweenstartDateandendDategetFlashPromotionSession(Date): ChecksSmsFlashPromotionSessionwhere the current time (extracted viaDateUtil.getTime) lies withinstartTimeandendTimegetNextFlashPromotionSession(Date): Retrieves the earliest future session usingstart_time > dateordered ascending
The homeDao.getFlashProductList method (implemented in SmsFlashPromotionProductRelationDao.java) performs a three-table join to return FlashPromotionProduct DTOs containing product details and flash-specific pricing.
Data Model Relationships
The database schema follows a strict one-to-many hierarchy:
SmsFlashPromotion (1) ── (*) SmsFlashPromotionSession (1) ── (*) SmsFlashPromotionProductRelation
- SmsFlashPromotion: Defines campaign duration via
start_dateandend_datecolumns - SmsFlashPromotionSession: References
flashPromotionIdand defines dailystart_time/end_timeboundaries - SmsFlashPromotionProductRelation: Contains foreign keys to both parent tables plus pricing and inventory fields (
flash_promotion_price,flash_promotion_count)
The portal’s FlashPromotionProduct DTO extends PmsProduct to combine catalog data with flash sale attributes, creating a ready-to-render payload for the frontend home page.
Practical Code Examples
Retrieving Current Flash Sales (Portal)
Client applications access flash data through the content service:
HomeContentResult home = homeService.content();
HomeFlashPromotion flashBlock = home.getHomeFlashPromotion();
if (flashBlock != null) {
System.out.println("Current flash sale ends at " + flashBlock.getEndTime());
flashBlock.getProductList().forEach(p ->
System.out.println(p.getName() + " – " + p.getFlashPromotionPrice()));
}
Key Source Files
Critical implementation files include:
- Models:
mall-mbg/src/main/java/com/macro/mall/model/SmsFlashPromotion.java,SmsFlashPromotionSession.java,SmsFlashPromotionProductRelation.java - Admin Services:
mall-admin/src/main/java/com/macro/mall/service/impl/SmsFlashPromotionServiceImpl.java,SmsFlashPromotionSessionServiceImpl.java - Portal Service:
mall-portal/src/main/java/com/macro/mall/portal/service/impl/HomeServiceImpl.java - DAO:
mall-admin/src/main/java/com/macro/mall/dao/SmsFlashPromotionProductRelationDao.java
Summary
- Three-tier architecture: Separates campaign definition (
SmsFlashPromotion), time windows (SmsFlashPromotionSession), and product assignments (SmsFlashPromotionProductRelation) - Admin control: Full CRUD operations via REST endpoints in
mall-adminwith status management and pagination via PageHelper - Portal rendering:
HomeServiceImpl#getHomeFlashPromotion()queries active promotions by date and time, then loads associated products through custom DAO joins - Inventory protection: Product relations include both total stock (
flashPromotionCount) and per-user limits (flashPromotionLimit) to prevent overselling - Temporal precision: The system distinguishes between promotion dates (calendar days) and session times (clock times) to support recurring daily flash sales within multi-day campaigns
Frequently Asked Questions
How does the system prevent users from purchasing flash sale items outside designated time windows?
The portal validates temporal eligibility in HomeServiceImpl#getHomeFlashPromotion(), which queries SmsFlashPromotionSession where the current time falls between startTime and endTime. Only products linked to currently active sessions are returned to the frontend, effectively blocking premature or expired purchases at the data retrieval layer.
What is the difference between flashPromotionCount and flashPromotionLimit?
According to the SmsFlashPromotionProductRelation model, flashPromotionCount defines the total inventory available for the flash sale session (system-wide stock), while flashPromotionLimit restricts how many units a single user can purchase. This dual-threshold approach prevents individual users from depleting inventory while maintaining scarcity controls.
How does the portal handle multiple concurrent flash sale promotions?
The getFlashPromotion(Date) method in HomeServiceImpl selects the active promotion based solely on date range and status = 1. If multiple promotions overlap on the same date, the implementation returns the first match found. For session conflicts within a promotion, getFlashPromotionSession(Date) identifies the specific time window containing the current moment, ensuring only one session’s products display at any given time.
Where does the aggregation of flash sale product data occur?
The SmsFlashPromotionProductRelationDao.java interface defines getFlashProductList(Long promotionId, Long sessionId), which executes a SQL join across sms_flash_promotion, sms_flash_promotion_session, and product tables. This aggregation happens at the database level via MyBatis mappers, returning FlashPromotionProduct DTOs that combine relational data with base product attributes from PmsProduct.
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 →