Backend Development 20 min read

Design and Implementation of a Coupon System for a Rental Platform

This article presents a complete end‑to‑end design of a coupon system for a rental‑listing platform, covering business flow, database schema, activity management, coupon issuance, binding, user redemption, state‑machine implementation, caching strategies, stock deduction, redemption handling, and future scalability optimizations.

Top Architect
Top Architect
Top Architect
Design and Implementation of a Coupon System for a Rental Platform

The platform provides housing listings for merchants (B‑side) and exposure to customers (C‑side). To boost user acquisition during holidays, a coupon system is introduced where merchants create activities, bind coupons to houses, and users claim coupons to receive rental discounts.

1. Business Flow

Merchants create activities, select discount types, and bind coupons to specific houses. Users view discounted houses, claim coupons, and complete rental contracts using the coupons.

2. Technical Design

2.1 Activity Management

Activity data is stored in t_activity with fields such as activity time range, city IDs, coupon type, discount limits, and status. The table is created with the following SQL:

CREATE TABLE `t_activity` (
  `activeId` bigint(20) NOT NULL COMMENT '活动ID',
  `title` varchar(256) NOT NULL COMMENT '活动名称',
  `applyStartTime` timestamp NULL DEFAULT NULL COMMENT '报名开始时间',
  `applyEndTime` timestamp NULL DEFAULT NULL COMMENT '报名停止时间',
  `activityStartTime` timestamp NULL DEFAULT NULL COMMENT '活动开始时间',
  `activityEndTime` timestamp NULL DEFAULT NULL COMMENT '活动结束时间',
  `cityIds` varchar(256) NOT NULL COMMENT '覆盖城市,多个逗号分隔',
  `couponType` tinyint(4) NOT NULL DEFAULT '0' COMMENT '优惠类型,1 直减;2 折扣;3免费住N天;4免押金;5特价房',
  `lowerLimit` int NOT NULL DEFAULT 0 COMMENT '优惠数值下限',
  `upperLimit` int NOT NULL DEFAULT 0 COMMENT '优惠数值上限',
  `description` text COMMENT '活动描述',
  `cubeType` smallint(6) NOT NULL DEFAULT '1001' COMMENT '活动类型',
  `foreignId` bigint(20) NOT NULL DEFAULT '0' COMMENT '外部ID',
  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '活动状态',
  `createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `recordStatus` tinyint(4) NOT NULL DEFAULT '0' COMMENT '数据状态',
  PRIMARY KEY (`activeId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='活动信息表';

Activity status is updated every minute by a cron job that checks the current time range and modifies the status field. The status changes are propagated to coupons via MQ.

2.2 Coupon Issuance

Coupon metadata is stored in t_couponmeta . Important fields include couponType , discount values, usage thresholds, total amount, and status codes (10‑new, 20‑enabled, 30‑expired, 40‑ended, 50‑terminated).

CREATE TABLE `t_couponmeta` (
  `couponMetaId` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '券id',
  `appId` int(11) NOT NULL DEFAULT '1' COMMENT '区分建立来源',
  `activeId` bigint(20) NOT NULL DEFAULT '0' COMMENT '活动ID',
  `companyId` bigint(20) NOT NULL COMMENT '公司编号',
  `cityId` int(11) NOT NULL COMMENT '城市id',
  `companyName` varchar(255) DEFAULT NULL COMMENT '公司名称',
  `companyShortName` varchar(255) DEFAULT NULL COMMENT '公司简称',
  `couponType` tinyint(4) NOT NULL COMMENT '优惠券类型',
  `title` varchar(256) NOT NULL COMMENT '优惠券名称',
  `directDiscount` int(11) NOT NULL DEFAULT '0' COMMENT '直减券优惠力度',
  `discount` int(11) NOT NULL DEFAULT '0' COMMENT '折扣力度',
  `freeLive` int(11) NOT NULL DEFAULT '0' COMMENT '免费住n天券',
  `threshold` varchar(256) NOT NULL COMMENT '使用门槛',
  `deduction` tinyint(4) NOT NULL DEFAULT '1' COMMENT '抵扣说明',
  `totalAmount` int(11) NOT NULL DEFAULT '0' COMMENT '券总数',
  `applyAmount` int(11) NOT NULL DEFAULT '0' COMMENT '已领取总数',
  `activityStartTime` timestamp NULL DEFAULT NULL COMMENT '活动开始时间',
  `activityEndTime` timestamp NULL DEFAULT NULL COMMENT '活动结束时间',
  `startTime` timestamp NULL DEFAULT NULL COMMENT '券使用开始时间',
  `expireTime` timestamp NULL DEFAULT NULL COMMENT '券使用结束时间',
  `status` int(11) NOT NULL DEFAULT '10' COMMENT '状态码',
  `expireType` tinyint(4) NOT NULL DEFAULT '1' COMMENT '有效期类型',
  `validPeriod` tinyint(4) NOT NULL DEFAULT '0' COMMENT '浮动有效期(天)',
  `tenantRange` tinyint(1) NOT NULL DEFAULT '1' COMMENT '租客范围',
  `customScope` varchar(256) NOT NULL DEFAULT '' COMMENT '自定义租客范围',
  `comment` varchar(50) DEFAULT NULL COMMENT '备注',
  `cubeType` smallint(6) NOT NULL DEFAULT '1001' COMMENT '活动类型',
  `updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `recordStatus` tinyint(4) DEFAULT '0' COMMENT '状态',
  PRIMARY KEY (`couponMetaId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券表';

Both activity and coupon data are cached using a proxy pattern; read‑heavy requests hit Redis first, while writes go through a message‑queue to keep DB and cache consistent.

2.3 Coupon‑House Binding

The binding relationship is stored in t_bindcoupon , which records which house a coupon is attached to and its current binding status.

CREATE TABLE `t_bindcoupon` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `couponMetaId` int(11) NOT NULL COMMENT '券id',
  `companyId` bigint(20) NOT NULL COMMENT '公司编号',
  `activityStatus` tinyint(4) NOT NULL COMMENT '状态 0‑准备中 1‑活动中 2‑结束未失效 3‑结束失效',
  `houseId` bigint(20) NOT NULL DEFAULT '0' COMMENT '房源id',
  `recordStatus` tinyint(4) NOT NULL COMMENT '数据状态 0 有效,-1 失效',
  `createTime` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `updateTime` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_companyId_couponMetaId` (`companyId`,`couponMetaId`),
  KEY `idx_planeId` (`planeId`),
  KEY `idx_houseid_activitystatus` (`houseId`,`activityStatus`)
) ENGINE=InnoDB AUTO_INCREMENT=17379 DEFAULT CHARSET=utf8 COMMENT='优惠券绑定范围表';

Binding operations use a distributed lock to prevent over‑binding, and observers listen to state changes to update house indexes, cache, and DB synchronously via MQ.

2.4 User Coupon Claim

Claiming follows three steps: request validation (using a Bloom filter in Redis), atomic stock deduction in Redis, and a transactional write of claim record and stock update to MySQL. Redis guarantees atomicity for high concurrency, while MySQL updates are serialized through a message‑task table to avoid lock contention.

Four possible outcomes are handled:

Redis succeeds, DB write fails → rollback Redis stock.

Redis fails (no stock or outage) → no DB operation.

Redis succeeds, DB crashes before commit → potential under‑claim; mitigated by async reconciliation.

Redis master fails after stock deduction, DB reads from replica → possible over‑claim; mitigated by periodic consistency checks.

Additional safeguards include periodic stock‑freeze when remaining inventory drops below 5 % and asynchronous verification tasks to align Redis and DB counts.

2.5 Coupon Redemption

Redemption is exposed as a microservice with three states: unused, locked, and used. When an order is placed, the coupon is locked; upon order completion it becomes used, otherwise it reverts to unused.

Future Optimizations

Shard user‑coupon data across databases for read/write scalability.

Deploy Redis in a sharded cluster to increase capacity and availability.

Split the service into multiple pods behind a gateway to distribute load.

Introduce jd‑hotkey for hot‑key local caching.

Use Canal to sync binlog changes to cache and trigger downstream updates.

Conclusion

The complete coupon system follows the principle “read‑heavy use cache, write‑heavy use queue”. Activity and coupon data are pushed to Redis proactively, state transitions are modeled with the State pattern, observers keep related data consistent, and stock deduction leverages Redis atomicity while ensuring eventual consistency with MySQL through asynchronous tasks.

distributed systemsbackend architectureMicroservicescachingDatabase Designcoupon system
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.