feat(coupon): 新增营销模块 gym-coupon,实现 PRD 2.5 优惠券/拼团/秒杀/营销/积分

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
时舟年
2026-06-15 09:14:17 +08:00
parent 7a94145819
commit af2422c114
132 changed files with 9977 additions and 2 deletions
@@ -0,0 +1,74 @@
-- ============================================
-- 优惠券模块表结构(模块5.1 优惠券管理)
-- ============================================
-- 优惠券模板表
CREATE TABLE IF NOT EXISTS coupon_template (
id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
coupon_type VARCHAR(20) NOT NULL,
discount_value DECIMAL(10, 2) NOT NULL,
threshold_amount DECIMAL(10, 2) DEFAULT 0,
validity_type VARCHAR(20) NOT NULL,
start_time TIMESTAMP,
end_time TIMESTAMP,
valid_days INTEGER,
apply_scope VARCHAR(20) NOT NULL DEFAULT 'ALL',
apply_product_ids TEXT,
total_quantity INTEGER DEFAULT -1,
per_user_limit INTEGER DEFAULT 1,
stackable BOOLEAN DEFAULT FALSE,
claim_code VARCHAR(50),
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
issued_count INTEGER DEFAULT 0,
used_count INTEGER DEFAULT 0,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
COMMENT ON TABLE coupon_template IS '优惠券模板表';
COMMENT ON COLUMN coupon_template.coupon_type IS '类型:CASH-满减券, DISCOUNT-折扣券, COURSE-课程券, EXPERIENCE-体验券';
COMMENT ON COLUMN coupon_template.discount_value IS '优惠值:满减/课程/体验为金额,折扣券为折扣比例(如0.9表示9折)';
COMMENT ON COLUMN coupon_template.threshold_amount IS '使用门槛金额(满X元可用)';
COMMENT ON COLUMN coupon_template.validity_type IS '有效期类型:FIXED_DATE-固定日期, DAYS_AFTER_CLAIM-领取后X天';
COMMENT ON COLUMN coupon_template.apply_scope IS '适用商品范围:ALL-全场通用, SPECIFIC-指定商品';
COMMENT ON COLUMN coupon_template.total_quantity IS '发放总量,-1表示不限量';
COMMENT ON COLUMN coupon_template.status IS '状态:DRAFT-草稿, ACTIVE-进行中, TERMINATED-已终止, EXPIRED-已过期';
CREATE INDEX idx_coupon_template_status ON coupon_template(status);
CREATE INDEX idx_coupon_template_type ON coupon_template(coupon_type);
CREATE INDEX idx_coupon_template_name ON coupon_template(name);
CREATE UNIQUE INDEX idx_coupon_template_claim_code ON coupon_template(claim_code) WHERE claim_code IS NOT NULL AND deleted_at IS NULL;
-- 会员优惠券表
CREATE TABLE IF NOT EXISTS member_coupon (
id BIGINT PRIMARY KEY,
template_id BIGINT NOT NULL,
member_id BIGINT NOT NULL,
coupon_code VARCHAR(64) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'AVAILABLE',
distribute_type VARCHAR(20) NOT NULL DEFAULT 'MANUAL',
received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
expire_at TIMESTAMP NOT NULL,
used_at TIMESTAMP,
order_id BIGINT,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
CONSTRAINT fk_member_coupon_template FOREIGN KEY (template_id) REFERENCES coupon_template(id)
);
COMMENT ON TABLE member_coupon IS '会员优惠券表';
COMMENT ON COLUMN member_coupon.status IS '状态:AVAILABLE-可用, USED-已使用, EXPIRED-已过期, INVALID-已作废';
COMMENT ON COLUMN member_coupon.distribute_type IS '发放方式:MANUAL-手动, BATCH-批量, AUTO-自动, CLAIM-领取码';
CREATE INDEX idx_member_coupon_template_id ON member_coupon(template_id);
CREATE INDEX idx_member_coupon_member_id ON member_coupon(member_id);
CREATE INDEX idx_member_coupon_status ON member_coupon(status);
CREATE UNIQUE INDEX idx_member_coupon_code ON member_coupon(coupon_code);
@@ -0,0 +1,219 @@
-- ============================================
-- 营销模块 5.2~5.5 表结构
-- ============================================
-- 5.2 拼团活动
CREATE TABLE IF NOT EXISTS group_buy_activity (
id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
product_type VARCHAR(20) NOT NULL,
product_id BIGINT NOT NULL,
product_name VARCHAR(200),
original_price DECIMAL(10, 2) NOT NULL,
group_price DECIMAL(10, 2) NOT NULL,
required_members INTEGER NOT NULL DEFAULT 2,
valid_hours INTEGER NOT NULL DEFAULT 24,
start_time TIMESTAMP NOT NULL,
end_time TIMESTAMP NOT NULL,
stock INTEGER DEFAULT -1,
sold_count INTEGER DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS group_buy_team (
id BIGINT PRIMARY KEY,
activity_id BIGINT NOT NULL,
leader_member_id BIGINT NOT NULL,
required_members INTEGER NOT NULL,
current_members INTEGER NOT NULL DEFAULT 1,
status VARCHAR(20) NOT NULL DEFAULT 'FORMING',
expire_at TIMESTAMP NOT NULL,
success_at TIMESTAMP,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
CONSTRAINT fk_group_buy_team_activity FOREIGN KEY (activity_id) REFERENCES group_buy_activity(id)
);
CREATE TABLE IF NOT EXISTS group_buy_participant (
id BIGINT PRIMARY KEY,
team_id BIGINT NOT NULL,
activity_id BIGINT NOT NULL,
member_id BIGINT NOT NULL,
is_leader BOOLEAN DEFAULT FALSE,
status VARCHAR(20) NOT NULL DEFAULT 'JOINED',
join_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
CONSTRAINT fk_group_buy_participant_team FOREIGN KEY (team_id) REFERENCES group_buy_team(id)
);
CREATE INDEX idx_group_buy_activity_status ON group_buy_activity(status);
CREATE INDEX idx_group_buy_team_activity ON group_buy_team(activity_id);
CREATE INDEX idx_group_buy_team_status ON group_buy_team(status);
CREATE INDEX idx_group_buy_participant_member ON group_buy_participant(member_id);
-- 5.3 秒杀活动
CREATE TABLE IF NOT EXISTS flash_sale_activity (
id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
start_time TIMESTAMP NOT NULL,
end_time TIMESTAMP NOT NULL,
pay_timeout_minutes INTEGER DEFAULT 5,
per_user_limit INTEGER DEFAULT 1,
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS flash_sale_item (
id BIGINT PRIMARY KEY,
activity_id BIGINT NOT NULL,
product_type VARCHAR(20) NOT NULL,
product_id BIGINT NOT NULL,
product_name VARCHAR(200) NOT NULL,
original_price DECIMAL(10, 2) NOT NULL,
seckill_price DECIMAL(10, 2) NOT NULL,
stock INTEGER NOT NULL DEFAULT 0,
sold_count INTEGER DEFAULT 0,
per_user_limit INTEGER DEFAULT 1,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
CONSTRAINT fk_flash_sale_item_activity FOREIGN KEY (activity_id) REFERENCES flash_sale_activity(id)
);
CREATE TABLE IF NOT EXISTS flash_sale_order (
id BIGINT PRIMARY KEY,
activity_id BIGINT NOT NULL,
item_id BIGINT NOT NULL,
member_id BIGINT NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
pay_amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
expire_at TIMESTAMP NOT NULL,
pay_at TIMESTAMP,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
CONSTRAINT fk_flash_sale_order_item FOREIGN KEY (item_id) REFERENCES flash_sale_item(id)
);
CREATE INDEX idx_flash_sale_activity_status ON flash_sale_activity(status);
CREATE INDEX idx_flash_sale_item_activity ON flash_sale_item(activity_id);
CREATE INDEX idx_flash_sale_order_member ON flash_sale_order(member_id);
CREATE INDEX idx_flash_sale_order_status ON flash_sale_order(status);
-- 5.4 会员营销活动
CREATE TABLE IF NOT EXISTS marketing_activity (
id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
activity_type VARCHAR(30) NOT NULL,
start_time TIMESTAMP NOT NULL,
end_time TIMESTAMP NOT NULL,
discount_value DECIMAL(10, 2),
threshold_amount DECIMAL(10, 2) DEFAULT 0,
gift_description TEXT,
apply_scope VARCHAR(20) DEFAULT 'ALL',
apply_product_ids TEXT,
rules_json TEXT,
participant_count INTEGER DEFAULT 0,
order_count INTEGER DEFAULT 0,
total_discount_amount DECIMAL(12, 2) DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE INDEX idx_marketing_activity_type ON marketing_activity(activity_type);
CREATE INDEX idx_marketing_activity_status ON marketing_activity(status);
-- 5.5 积分商城
CREATE TABLE IF NOT EXISTS points_rule (
id BIGINT PRIMARY KEY,
rule_name VARCHAR(100) NOT NULL,
rule_type VARCHAR(30) NOT NULL,
points_value INTEGER DEFAULT 0,
ratio DECIMAL(10, 4),
description TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS points_mall_product (
id BIGINT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
description TEXT,
product_type VARCHAR(20) NOT NULL,
related_id BIGINT,
points_cost INTEGER NOT NULL,
stock INTEGER DEFAULT -1,
sold_count INTEGER DEFAULT 0,
image_url VARCHAR(500),
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS member_points (
id BIGINT PRIMARY KEY,
member_id BIGINT NOT NULL UNIQUE,
total_points INTEGER DEFAULT 0,
available_points INTEGER DEFAULT 0,
used_points INTEGER DEFAULT 0,
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS points_record (
id BIGINT PRIMARY KEY,
member_id BIGINT NOT NULL,
change_type VARCHAR(20) NOT NULL,
points INTEGER NOT NULL,
balance_after INTEGER NOT NULL,
source VARCHAR(50),
related_id BIGINT,
remark VARCHAR(500),
create_by VARCHAR(50),
update_by VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE INDEX idx_points_mall_product_status ON points_mall_product(status);
CREATE INDEX idx_member_points_member ON member_points(member_id);
CREATE INDEX idx_points_record_member ON points_record(member_id);