优惠券管理

同学们,之前几天我们实现的都是学习辅助功能。学习辅助的目的是提升用户的学习体验,维护好老用户。而一个网站要想长久发展,除了要服务好老用户,还必须能吸引新用户,也就是拉新。而拉新最常见的手段就是优惠促销,比如优惠券。

从今天开始,我们就一起来实现一个优惠券管理的服务。优惠券功能比较复杂,包含的业务亮点也非常多,例如:

  • 优惠券的优惠策略设计
  • 优惠券的兑换码算法
  • 优惠券领取的并发安全问题及解决方案
  • 优惠券叠加方案的智能推荐
  • 多商品、多券叠加时的优惠金额计算
  • 多商品订单退款的拆单和退券问题
    等等。。
    这些方案不仅仅是在咱们项目,在所有电商类型的项目中都是热点、难点问题,在接下来几天的学习中我们会逐一分析和解决。

1.需求分析
需求分析的流程与以往类似,还是基于产品原型,三步走:

  • 分析业务流程
  • 统计业务接口
  • 设计数据库表

1.1.业务流程梳理
优惠券包括两大部分功能:

  • 优惠券管理和发放(管理端)
  • 优惠券的领取和使用(用户端)

我们今天先实现管理端的功能。在后台管理营销中心的优惠券管理页面,可以看到一个优惠券列表页:
[图片]

我们可以在这里实现优惠券的基础的增删改查功能。
不过,新增的优惠券并不会立刻出现在用户端页面,管理员还需要对优惠券信息做审核,审核通过后则可以通过发放按钮来发布优惠券。

而优惠券的发布也有两种不同的方式:
在这里插入图片描述

一个是立刻发放,一个是定时发放。
顾明思议,立刻发放就是优惠券立刻生效,会直接出现在用户端页面供用户领取。定时发放则需要指定一个发放开始时间,时间到期后才会进入出现在用户端页面。

而且无论是哪种发放方式,都需要指定一个过期时间,当优惠券过期后就会进入已结束状态,用户端页面无法领取。

当然,除了过期导致的结束发放以外,管理员也可以手动点击暂停发放:
[图片]

也可以在需要的时候重新发放优惠券。

特别需要注意的是,优惠券的领取方式有两种,来看一下优惠券的新增表单:
[图片]

领取方式有两种:

  • 手动领取:就是展示在用户端页面,由用户自己手动点击领取
  • 指定方法:就是兑换码模式,后台给优惠券生成N张兑换码,由管理员发放给指定用户。

这就要求我们在发放优惠券的时候做判断,如果发现是指定发放模式,则需要提前生成兑换码。

综上,优惠券管理的业务流程和优惠券的状态转换如图:
暂时无法在Hi ECARX文档外展示此内容

1.2.接口统计
首先,在优惠券的列表页:
[图片]

页面规范如下:

  1. 搜索条件
  • 优惠类型:天机学堂支持的类型有 1:满减,2:每满减,3:折扣,4:无门槛
  • 优惠券状态:包括 1:待发放,2:未开始 3:进行中,4:已结束,5:暂停
  1. 列表显示
  • 默认显示10条
  • 默认按照创建时间倒序排序
  • 使用/领取/发放:优惠券数量统计,已使用的数量/已领取的数量/总发放数量
  • 领用期限:就是券领取的开始和结束时间
    可见这个列表就是一个典型的带过滤条件的分页查询。其它增删改查接口都比较简单,不再赘述。

所有接口在页面都一目了然:

  • 优惠券的基本管理接口:
    • 分页查询优惠券列表
    • 新增优惠券
    • 编辑优惠券
    • 查看优惠券(根据id查询优惠券)
    • 删除优惠券
  • 优惠券的方法接口:
    • 发放优惠券
    • 暂停发放优惠券

另外有几个比较隐蔽的接口。一个是发放优惠券时,如果选择的是定时方法,则需要指定发放时间,到期后才发放。这就需要一个定时任务,检索优惠券表,找到发放时间到期的券,完成发放功能。
另一个也是发放券问题,券除了有发放时间,还有过期时间,因此需要一个定时任务,检查券的过期时间,发现到期后需要更新券状态。
以上两个都是定时任务接口:

  • 定时发放优惠券
  • 定时结束优惠券发放

还有一个是跟兑换码有关。就是在发放优惠券的时候,如果发现优惠券的领取方式是指定发放,则需要生成兑换码。因此页面有一个查询兑换码功能:
[图片]

当我们点击查看兑换码时,就会进入一个兑换码展示页面:
[图片]

可以看出来,这是一个有过滤条件的分页查询功能。

综上,优惠券相关接口包括:

编号
接口简述
优惠券管理
1
新增优惠券

2
修改优惠券

3
分页查询优惠券

4
根据id查询优惠券

5
删除优惠券
优惠券发放
1
发放优惠券

2
暂停发放

3
查询兑换码
定时任务
1
定时发放优惠券

2
定时结束优惠券
在这里插入图片描述

1.3.表结构设计
通过前面的接口分析,发现接口主要跟两个实体有关:

  • 优惠券
  • 兑换码

所以,接下来要设计的表就是以上两张表。

1.3.1.优惠券
首先从优惠券的新增表单来分析,表单页面如下:
[图片]

其中的字段包含:

  • 优惠券名称:一个普通字符串

  • 使用范围:这里有两种选择:全部课程、指定课程分类,也就是不限定课程、限定课程,可以用布尔类型来表示。不过一旦选定了课程分类,就需要指定真正限定的分类。
    [图片]

    此处是允许多选的,也就是说一个优惠券可以限定多个课程分类。而一个分类也可能被不同的券作为限定范围。因此优惠券与限定的分类是多对多关系。需要一张中间表来保存关系。这个以后再说。

  • 优惠券类型:包含满减、每满减、满折扣、无门槛四种,例如:

    • 满100减15
    • 每满100减10
    • 满200打8折,不超过50
    • 直减20
      可以看出来,虽然规则不同,但都可以用以下几部分来表示:
    • 优惠的门槛:比如满100的100
    • 优惠值:比如减15的15、打8折的8
    • 优惠上限:比如不超过50
      因此,我们完全要表示完整优惠策略就需要四个字段:优惠类型、优惠门槛、优惠值、优惠上限
  • 推广方式:手动领取和指定发放

  • 发放数量

  • 每人限领数量

OK,表单中的字段就这么多。然后再看看分页页码:
[图片]

与新增页面重复的就不再赘述了,这里多出的一些字段有:

  • 已领取数量

  • 已使用数量

  • 领用期限:也就是优惠券开始发放、结束发放的时间

  • 使用期限:用户领取券后的使用期限,有两种方式:
    [图片]

    • 固定时间段:需要指定开始时间、结束时间
    • 固体天数:指定天数,从用户领取之日起计算
  • 优惠券状态

综上,优惠券表结构如下:
– 导出 表 tj_promotion.coupon 结构

CREATE TABLE IF NOT EXISTS `coupon` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '优惠券id',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '优惠券名称,可以和活动名称保持一致',
  `type` tinyint NOT NULL DEFAULT '1' COMMENT '优惠券类型,1:普通券。目前就一种,保留字段',
  `discount_type` tinyint NOT NULL COMMENT '折扣类型,1:满减,2:每满减,3:折扣,4:无门槛',
  `specific` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否限定作用范围,false:不限定,true:限定。默认false',
  `discount_value` int NOT NULL DEFAULT '1' COMMENT '折扣值,如果是满减则存满减金额,如果是折扣,则存折扣率,8折就是存80',
  `threshold_amount` int NOT NULL DEFAULT '0' COMMENT '使用门槛,0:表示无门槛,其他值:最低消费金额',
  `max_discount_amount` int NOT NULL DEFAULT '0' COMMENT '最高优惠金额,满减最大,0:表示没有限制,不为0,则表示该券有金额的限制',
  `obtain_way` tinyint NOT NULL DEFAULT '0' COMMENT '获取方式:1:手动领取,2:兑换码',
  `issue_begin_time` datetime DEFAULT NULL COMMENT '开始发放时间',
  `issue_end_time` datetime DEFAULT NULL COMMENT '结束发放时间',
  `term_days` int NOT NULL DEFAULT '0' COMMENT '优惠券有效期天数,0:表示有效期是指定有效期的',
  `term_begin_time` datetime DEFAULT NULL COMMENT '优惠券有效期开始时间',
  `term_end_time` datetime DEFAULT NULL COMMENT '优惠券有效期结束时间',
  `status` tinyint DEFAULT '1' COMMENT '优惠券配置状态,1:待发放,2:未开始   3:进行中,4:已结束,5:暂停',
  `total_num` int NOT NULL DEFAULT '0' COMMENT '总数量,不超过5000',
  `issue_num` int NOT NULL DEFAULT '0' COMMENT '已发行数量,用于判断是否超发',
  `used_num` int NOT NULL DEFAULT '0' COMMENT '已使用数量',
  `user_limit` int NOT NULL DEFAULT '1' COMMENT '每个人限领的数量,默认1',
  `ext_param` json DEFAULT NULL COMMENT '拓展参数字段,保留字段',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `creater` bigint NOT NULL COMMENT '创建人',
  `updater` bigint NOT NULL COMMENT '更新人',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1630563495906942979 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='优惠券的规则信息';

另外,用来表示优惠券使用范围时,需要一个优惠券与课程分类的中间关系表:

CREATE TABLE IF NOT EXISTS `coupon_scope` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `type` tinyint NOT NULL COMMENT '范围限定类型:1-分类,2-课程,等等',
  `coupon_id` bigint NOT NULL COMMENT '优惠券id',
  `biz_id` bigint NOT NULL COMMENT '优惠券作用范围的业务id,例如分类id、课程id',
  PRIMARY KEY (`id`),
  KEY `idx_coupon` (`coupon_id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='优惠券作用范围信息';

1.3.2.兑换码
兑换码的作用是让用户拿着这个码来兑换一张优惠券。因此一定与两个实体有关:

  • 优惠券
  • 用户
    也就是说,我们需要知道将来是谁来兑换的券,可以兑换哪张券。当然,兑换码的码肯定也要保持到数据库,长这样:
    [图片]

除此以外,为了避免码被重复兑换,我们还需要记录码的状态:

  • 码状态:已兑换、未兑换
    最后,兑换码同样是有过期时间的,这个时间应该跟优惠券的过期时间一致。

综上,兑换码的最终表结构:

CREATE TABLE IF NOT EXISTS `exchange_code` (
  `id` int NOT NULL COMMENT '兑换码id',
  `code` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '兑换码',
  `status` tinyint NOT NULL DEFAULT '1' COMMENT '兑换码状态, 1:待兑换,2:已兑换,3:兑换活动已结束',
  `user_id` bigint NOT NULL DEFAULT '0' COMMENT '兑换人',
  `type` tinyint NOT NULL DEFAULT '1' COMMENT '兑换类型,1:优惠券,以后再添加其它类型',
  `exchange_target_id` bigint NOT NULL DEFAULT '0' COMMENT '兑换码目标id,例如兑换优惠券,该id则是优惠券的配置id',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `expired_time` datetime NOT NULL COMMENT '兑换码过期时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `index_status` (`status`) USING BTREE,
  KEY `index_config_id` (`exchange_target_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='兑换码';

所有的SQL文件都在课前资料中提供了:
[图片]

1.4.代码生成
首先,在DEV分支的基础上创建一个新的功能分支:
git checkout -b feature-promotions

1.4.1.创建新的模块
优惠券功能属于优惠促销的一部分,在项目中肯定属于独立的功能模块。我们需要创建一个新的module:
[图片]

选择Maven工程:
[图片]

然后填写项目信息:

[图片]

点击Finish,完成模块创建:
[图片]

1.4.2.基础配置
项目创建完毕后,需要引入依赖,POM文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>



tjxt
com.tianji
1.0.0

4.0.0

<artifactId>tj-promotion</artifactId>

<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
    <!--auth-sdk-->
    <dependency>
        <groupId>com.tianji</groupId>
        <artifactId>tj-auth-resource-sdk</artifactId>
        <version>1.0.0</version>
    </dependency>
    <!--api-->
    <dependency>
        <groupId>com.tianji</groupId>
        <artifactId>tj-api</artifactId>
        <version>1.0.0</version>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--Redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--redisson-->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
    </dependency>
    <!--discovery-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--config-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <!--caffeine本地缓存-->
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
    </dependency>
    <!--xxl-job-->
    <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-job-core</artifactId>
    </dependency>
    <!--loadbalancer-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
</dependencies>
<build>
    <finalName>${project.artifactId}</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值