【实战】积分跟优惠券的设计
# 积分跟优惠券的设计
# 积分表设计
得想想积分模块通常需要哪些功能。用户赚取积分、消费积分、积分明细记录,可能还有积分规则和等级制度之类的。所以,表结构应该覆盖这些方面。
首先,用户积分账户表,应该是每个用户有一个账户,记录当前积分和冻结积分。冻结积分可能是因为有未完成的订单,暂时不能使用。这里主键应该是用户ID,或者单独设置一个账户ID?用户ID作为主键的话,可能更方便查询,但如果有分表分库的需求,可能需要单独的账户ID。不过用户ID一般是唯一的,可能直接用用户ID作为主键就行。字段的话,包括总积分、可用积分、冻结积分、更新时间这些。需要确保并发操作时的数据一致性,所以可能需要乐观锁或者版本号字段。
然后是积分明细表,记录每一次积分的变动。这里需要明细类型,比如获得、消费、过期等。积分值可以是正数或负数,比如消费是负数。相关的业务类型和业务ID,比如订单ID,活动ID之类的,方便以后查询。状态字段可能用来标识这笔明细是否有效,比如可能有些积分被撤销的情况。另外,需要记录生效时间和过期时间,这样处理积分过期的时候可以筛选出需要过期的记录。索引的话,可能需要在用户ID和业务类型上建索引,加快查询速度。
接下来是积分规则表,不同的积分获取或消费规则,比如签到送多少积分,订单金额兑换比例,或者不同等级的用户积分倍率。规则类型字段区分是获取还是消费,适用等级可能关联用户等级表,生效时间和失效时间控制规则的启用状态。这里可能需要一个规则内容字段,存储JSON格式的配置,比如{"points": 10}或者{"exchange_rate": 0.1},这样扩展性更好,不需要频繁改表结构。
积分等级表的话,可能需要等级名称、所需积分下限、权益描述等。这样用户达到一定积分后可以升级,享受不同的权益。权益可能也是JSON存储,方便扩展。
然后是积分冻结明细表,比如用户下单使用积分,但订单未完成时,需要冻结这部分积分,防止重复使用。冻结和解冻的时候需要记录关联的业务ID,比如订单ID,以及状态,比如冻结中、已解冻、已扣减。这样在处理订单完成或取消时,可以相应地处理冻结的积分。
关于扩展性,可能需要考虑模块化设计,各表之间通过外键关联,但实际中可能为了分库分表不用外键约束。使用状态字段和类型字段来区分不同的业务场景,避免每增加一个业务就需要新增字段。JSON字段的运用可以存储动态的规则内容,减少表结构变更。
在事务处理方面,积分账户的增减和明细记录需要在一个事务里,保证数据一致性。比如用户消费积分时,先扣减可用积分,增加冻结积分,同时记录明细。这里需要考虑并发问题,比如多个操作同时修改同一个用户的积分账户,可能需要使用乐观锁机制,通过版本号来控制。
# 设计要点及原因
- 账户与明细分离
- 为什么:避免高频更新导致锁表,保证账户汇总效率
- 如何做:用
积分账户表
存实时总额,积分明细表
记录流水
- 业务关联可追溯
- 为什么:需追踪积分来源(如订单ID、活动ID)
- 如何做:添加
业务类型
+业务ID
字段,支持多场景扩展
- 灵活规则配置
- 为什么:不同活动/等级积分规则可能频繁变化
- 如何做:用
JSON字段
存储动态规则,避免频繁改表
- 过期与冻结机制
- 为什么:防止积分滥用,需支持预冻结和自动过期
- 如何做:通过
状态字段
+过期时间
+ 定时任务实现
- 并发安全
- 为什么:防止超额兑换
- 如何做:通过
乐观锁
(版本号)或SELECT FOR UPDATE
控制
# 表设计(MySQL)
# 积分账户表(points_account)
CREATE TABLE points_account (
user_id BIGINT UNSIGNED PRIMARY KEY COMMENT '用户ID',
total_points INT NOT NULL DEFAULT 0 COMMENT '累计积分(含已过期)',
available_points INT NOT NULL DEFAULT 0 COMMENT '可用积分',
frozen_points INT NOT NULL DEFAULT 0 COMMENT '冻结积分',
late_points int not null default 0 commit '要过期积分(30天内 或者按月份来)',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) COMMENT '积分账户汇总';
2
3
4
5
6
7
8
9
索引:user_id
主键天然有索引
# 积分明细表(points_detail)
CREATE TABLE points_detail (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
points INT NOT NULL COMMENT '变动积分(正负值)',
type TINYINT NOT NULL COMMENT '类型:1-获取 2-消费 3-过期',
biz_type VARCHAR(32) NOT NULL COMMENT '业务类型:sign/daily_login/order...',
biz_id VARCHAR(64) NOT NULL COMMENT '业务ID(如订单ID)',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-有效 2-已撤销',
description VARCHAR(255) COMMENT '描述',
effect_time DATETIME NOT NULL COMMENT '生效时间',
expire_time DATETIME COMMENT '过期时间(可为空)',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) COMMENT '积分明细流水';
2
3
4
5
6
7
8
9
10
11
12
13
索引:
idx_user_id_type
(user_id
,type
) → 快速查用户某类积分idx_biz
(biz_type
,biz_id
) → 业务溯源
# 积分规则表(points_rule)
CREATE TABLE points_rule (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
rule_type TINYINT NOT NULL COMMENT '规则类型:1-赚取 2-消费',
name VARCHAR(64) NOT NULL COMMENT '规则名称',
config JSON NOT NULL COMMENT '动态配置(如{"points":10, "daily_max":100})',
min_level INT DEFAULT 0 COMMENT '适用最低等级',
effect_start DATETIME NOT NULL,
effect_end DATETIME NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
) COMMENT '积分规则配置';
2
3
4
5
6
7
8
9
10
索引:idx_rule_type
(rule_type
) + 时间范围查询优化
# 积分等级表(points_level)
CREATE TABLE points_level (
level INT UNSIGNED PRIMARY KEY COMMENT '等级',
level_name VARCHAR(32) NOT NULL COMMENT '等级名称',
min_points INT NOT NULL COMMENT '最低积分要求',
rights JSON COMMENT '权益配置(如{"discount":0.9})'
) COMMENT '积分等级配置';
2
3
4
5
6
# 积分冻结记录(points_frozen)
CREATE TABLE points_frozen (
freeze_id VARCHAR(64) PRIMARY KEY COMMENT '冻结ID(业务生成)',
user_id BIGINT UNSIGNED NOT NULL,
points INT NOT NULL COMMENT '冻结积分',
biz_type VARCHAR(32) NOT NULL COMMENT '关联业务类型',
biz_id VARCHAR(64) NOT NULL COMMENT '关联业务ID',
status TINYINT NOT NULL DEFAULT 1 COMMENT '1-冻结中 2-已解冻 3-已扣减',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) COMMENT '积分冻结记录';
2
3
4
5
6
7
8
9
10
# 优惠券设计
确定优惠券模块的核心需求。
优惠券通常包括
创建、发放、使用、过期等流程。
优惠券的基本信息(类型、面值、使用条件)、
发放规则(用户领取、系统发放)、
使用限制(商品、分类、时间)、
状态管理(未使用、已使用、过期)以及核销记录。
此外,扩展性很重要,比如支持不同类型的优惠券(满减、折扣、礼品券),以及未来可能新增的使用条件。
在设计表时,需要注意以下几点:
优惠券信息与实例分离:优惠券模板和用户持有的优惠券实例分开存储,这样同一个模板可以生成多个实例,避免重复数据,同时方便管理状态。
灵活的使用条件:使用JSON字段存储条件,比如满减金额、适用商品分类等,避免频繁修改表结构。
使用限制与核销记录:记录优惠券的使用时间、订单ID等,便于核对和退款处理。同时需要处理并发使用的问题,防止重复使用。
状态管理与过期处理:优惠券可能有未使用、已使用、已过期等状态,需要定时任务检查过期时间并更新状态。
发放机制:支持用户领取和系统自动发放,可能需要记录领取次数限制和用户领取记录。
# 设计要点及原因
- 模板与实例分离
- 为什么:同一优惠券模板可生成多个实例(如“新人礼包”发10万张券)
- 如何做:
coupon_template
存规则,coupon
存用户持有的具体券
- 动态使用规则
- 为什么:不同券的适用商品、金额门槛等差异大
- 如何做:用 JSON字段 存储规则(如
{"min_order_amount":100}
),避免频繁改表
- 状态与过期管理
- 为什么:需跟踪券是否已用、过期,且需定时任务自动失效
- 如何做:
status
字段标记状态,expire_time
控制过期,定时任务扫描更新
- 防超发与防超用
- 为什么:避免库存超发或用户重复使用同一张券
- 如何做:通过 数据库唯一约束 + 乐观锁 控制并发
- 核销溯源
- 为什么:需记录券的使用时间、订单、操作人,便于退款或审计
- 如何做:独立的
coupon_use_log
表记录核销流水
# 高扩展性表设计(MySQL)
# 1. 优惠券模板表(coupon_template)
CREATE TABLE coupon_template (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '模板ID',
name VARCHAR(64) NOT NULL COMMENT '券名称(如满100减20)',
type TINYINT NOT NULL COMMENT '类型:1-满减 2-折扣 3-礼品券',
rule_config JSON NOT NULL COMMENT '规则配置(如{"min_amount":100,"discount":20})',
total_stock INT UNSIGNED NOT NULL COMMENT '总库存',
remaining_stock INT UNSIGNED NOT NULL COMMENT '剩余库存',
user_limit TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '每人限领数量',
applicable_scope JSON COMMENT '适用范围(如{"category_ids":[1,2]})',
validity_type TINYINT NOT NULL COMMENT '有效期类型:1-固定时间 2-领取后N天有效',
start_time DATETIME COMMENT '有效期开始时间(类型1时需填)',
end_time DATETIME COMMENT '有效期结束时间(类型1时需填)',
valid_days INT UNSIGNED COMMENT '领取后有效天数(类型2时需填)',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-生效 2-停用',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) COMMENT '优惠券模板';
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
索引:
idx_status_type
(status
,type
) → 快速筛选可用券模板idx_validity
(start_time
,end_time
) → 有效期查询优化
# 2. 用户优惠券表(coupon)
CREATE TABLE coupon (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
template_id BIGINT UNSIGNED NOT NULL COMMENT '关联模板ID',
code VARCHAR(32) NOT NULL COMMENT '券唯一码(加密)',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-未使用 2-已使用 3-已过期',
obtain_time DATETIME NOT NULL COMMENT '领取时间',
expire_time DATETIME NOT NULL COMMENT '过期时间',
use_time DATETIME COMMENT '使用时间',
order_id BIGINT UNSIGNED COMMENT '使用的订单ID',
version INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
UNIQUE KEY uk_code (code),
KEY idx_user_status (user_id, status)
) COMMENT '用户持有的优惠券';
2
3
4
5
6
7
8
9
10
11
12
13
14
索引:
uk_code
唯一索引 → 防券码重复idx_user_status
→ 快速查用户可用券
# 3.优惠券核销记录表(coupon_use_log)
CREATE TABLE coupon_use_log (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
coupon_id BIGINT UNSIGNED NOT NULL COMMENT '券ID',
user_id BIGINT UNSIGNED NOT NULL,
order_id BIGINT UNSIGNED NOT NULL COMMENT '订单ID',
used_amount DECIMAL(10,2) NOT NULL COMMENT '抵扣金额',
use_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '使用时间',
operator_id BIGINT UNSIGNED COMMENT '操作人ID(系统/管理员)',
refund_time DATETIME COMMENT '退款时间',
KEY idx_coupon_order (coupon_id, order_id)
) COMMENT '券使用记录(支持退款溯源)';
2
3
4
5
6
7
8
9
10
11
# 4. 优惠券领取限制表(coupon_acquire_limit)(可选)
CREATE TABLE coupon_acquire_limit (
user_id BIGINT UNSIGNED NOT NULL,
template_id BIGINT UNSIGNED NOT NULL,
acquire_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已领取次数',
PRIMARY KEY (user_id, template_id)
) COMMENT '用户领取次数限制';
2
3
4
5
6