Design and Implementation of a Coupon System for a Rental Platform
This article details the business flow, database schema, micro‑service architecture, caching strategies, state‑machine implementation, and concurrency handling for building a robust coupon system that enables merchants to bind coupons to rental listings and attract end‑users.
Background: The platform is a rental listing service that needs a coupon system for merchant marketing during holidays, allowing merchants to bind coupons to listings and attract C‑end users.
Business flow: Activities are created, merchants sign up and generate coupons, coupons are bound to listings, users claim coupons, and upon signing contracts the coupons are applied.
Technical design: The system is divided into several modules with database tables for activities (t_activity), coupon metadata (t_couponmeta), and binding relations (t_bindcoupon). The tables are defined as:
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='活动信息表'; 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 '抵扣说明 1首月抵扣,2 平摊到月',
`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 '10:新建未启用,20:已启用,30:过期, 40 已结束 50 已中止',
`expireType` tinyint(4) NOT NULL DEFAULT '1' COMMENT '类型:1固定有效期类型,2浮动有效期类型',
`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 '状态 0默认 -1删除',
PRIMARY KEY (`couponMetaId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券表'; 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='优惠券绑定范围表';Data access uses the Proxy pattern to add a caching layer; read‑heavy operations are served from Redis, while writes go through MQ and asynchronous tasks to keep DB and cache consistent.
State transitions for activities and coupons are managed by a cron job and a state‑machine implemented with the State pattern; observers listen to state changes and propagate updates via MQ.
Coupon claiming on the C‑end employs Redis atomic decrement, Bloom filter checks, and optimistic locking in MySQL; edge cases such as over‑claiming or under‑claiming are mitigated with asynchronous reconciliation tasks.
Redemption is exposed as micro‑service APIs that lock, use, or roll back coupons based on order status.
Future optimizations include sharding coupon data, scaling Redis clusters, deploying hot‑key components, and using Canal for binlog‑driven cache synchronization.
In summary, the complete coupon system follows the principle of “read‑heavy use cache, write‑heavy use queue”, combines Proxy, State, and Observer patterns, and balances consistency and performance with Redis, MQ, and transactional DB updates.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.