专栏首页Grace development通用系统设计之优惠卷

通用系统设计之优惠卷

前言

本应该继续连载手撸框架系列文章的。但最近有一个需求 -> 优惠卷,之前很多朋友让我出一篇优惠卷相关的文章。这不,本章应了大伙的愿。开始我自己的表演 ??

额,这里还要插一句,有很多新人感觉在使用框架的过程中根本用不到PHP的很多概念,例如abstract,final 部分人感觉protected,private 都没有太大用处。更别提interface在框架中的使用了,感觉好无用处的举爪~

策略模式

优惠卷的存在到消亡至少要经历三个步骤(创建->使用->失效),以下为优惠卷完整生命周期图,

优惠卷有几百种几千种的优惠(骗人)方式(姿势),结合PHP代码来解决优惠卷应如何创建更合适,首先先创建一个类作为优惠卷的模版

class UserCouponTem
{
}

这个模版则是一个树根,未来所有优惠卷都要通过这个根去扩展,接下来创建一系列的优惠卷参数,例如与设计数据表一样,如下所示,通过成员变量的方式,束缚了优惠卷的具体字段。

/**
 * @var $couponName
 * @content 优惠券名称
 */
public $couponName;

/**
 * @var $alidityv
 * @content 有效期
 */
public $alidityv;

/**
 * @var $userId
 * @content 绑定的用户编码
 */
public $userId;

/**
 * @var $price
 * @content 抵扣金额
 */
public $price;

/**
 * @var $type
 * @content 类型 0 通用红包 1 查看扩展字段
 */
public $type;

/**
 * @var $extend
 * @content 扩展字段
 */
public $extend;

/**
 * @var $numbers
 * @content 卷号
 */
public $number;

/**
 * @var $content
 * @content 卷内容
 */
public $content;

优惠卷的模版创建完成后,接下来需要创建两个方法,第一个为服务提供者,规定每个创建优惠卷的类都必须存在create方法,没错,这是在写一个策略模式。

interface CouponInterface
{
    public function create($userId, $price);
}

public function provider(CouponInterface $coupon, $userId, $price)
{
    return $coupon->create($userId, $price);
}

最后是一个消费者

public function consumer($number)
{
    // $number 是卷号,这里一般都是操作redis,mysql的统一逻辑。
}

写好了一个简单的策略模式,那开始写一个策略吧。

使用策略

下方代码创建了一个通用红包。继承模版类中的字段并且去实现接口create方法

class Current extends UserCouponTem implements CouponInterface
{
    public function create($userId, $price)
    {
        $this->couponName = "通用红包";
        $this->alidityv   = "2019-01-09";
        $this->content    = "这是一个通用红包";
        $this->userId     = $userId;
        $this->price      = $price;
        $this->type       = 0;
        $this->extend     = [];
        $this->number     = '123456';

        return $this;
    }
}

最后通过下方代码创建一个通用红包,获得完整的一个优惠卷实例,最后将参数插入到数据库与用户表绑定则完成了一个基本的

$userCouponTem = new UserCouponTem();
$current       = $userCouponTem->provider(new Current(), $this->request->user_id,
$this->request->price);

设计思想

部分人会怀疑这种设计是多此一举,直接将逻辑设计到数据表不就OK了嘛?我们为何还要通过模版类,接口,服务提供者、服务容器去返回一个优惠卷实例?

试想不可能一次性将所有优惠卷的类型全部想到并且设计出来,数据表结构也不能频繁去更改。如何让一批代码适应整个业务并且对未来业务可扩展?这样的话则不能把所有逻辑存放到数据表中。这样做可能有以下几点好处

  • 可扩展性强,能够应对各种优惠卷的表达方式
  • 可维护性强,如果有新类型的业务可直接通过服务容器注入
  • 代码优雅,便于阅读,无论是新入职员工还是他人都很容易读写优惠卷的代码(比较优惠卷的业务实际很复杂)

上述实际就是Laravel的服务提供者、服务容器的概念,不明白的童鞋可去看文档并参考本例子。

数据结构

仅供参考(不是太认真的设计) 用户优惠卷表

CREATE TABLE `member_coupon` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '用户编码',
  `number` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '卷号',
  `content` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '卷内容',
  `price` decimal(8,2) NOT NULL COMMENT '金额',
  `alidityv` datetime NOT NULL COMMENT '到期时间',
  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '-1 过期 0 未使用 1 已使用',
  `use_date` int(11) NOT NULL DEFAULT '0' COMMENT '使用时间',
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

优惠卷记录表

CREATE TABLE `coupon_record` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '用户编码',
  `number` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '卷号',
  `price` decimal(8,2) NOT NULL COMMENT '金额',
  `json_content` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '具体json信息',
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

致谢

希望每篇文章并不是仅仅讲一件问题,我会把问题的扩展思想一并告诉大家,希望可以帮助到你。谢谢

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 老项目重构手记之用户系统

    重构首先要注意几个点 – 重构后功能的可扩展性 – 业务互相依赖的复杂度 – 脱离本身的业务进行重构 – 重构后的代码可读性与可维护性 – 性能的提升...

    CrazyCodes
  • 论某教育机构考试系统设计

    近期在做一套答题系统,参考了某教育机构的设计。本章跟大家聊聊考试系统中的核心 – 如何考试?

    CrazyCodes
  • 电商系统设计之用户系统

    设计以以下为工具讲起 – PHP为开发语言 – 基于Laravel框架 – MySQL为数据存储

    CrazyCodes
  • R语言中绘制箱形图的替代品:蜂群图和小提琴图

    箱形图  非常有用,因为它们不仅指示中间值,而且还显示了第一四分位数和第三四分位数的测量结果变化。但是,也有一些图提供了一些附加信息。在这里,我们将仔细研究箱形...

    拓端
  • MYSQL字符串截取总结:LEFT、RIGHT、SUBSTRING、SUBSTRING

    在实际的项目开发中有时会有对数据库某字段截取部分的需求,这种场景有时直接通过数据库操作来实现比通过代码实现要更方便快捷些,mysql有很多字符串函数可以用来处理...

    企鹅号小编
  • 刷爆朋友圈的 deepfakes 视频人物换脸是怎样炼成的?

    最近,一个名叫deepfakes的技术火了,这是一个可以给视频人物换脸的技术。 这张图片源自雷神三预告片索尔大战浩克的片段,将索尔的脸替换为了Trump的,这个...

    AI研习社
  • springboot-mybatisPlus整合

    很久没有写博客,最近项目中用到了springboot、mybatis,看到现在网上也有开源的mybatis-plus,干脆整合过来,该项目最终作为实现后台的模块...

    sucl
  • 只需一行代码,就能导入所有的Python库?

    GitHub地址:https://github.com/8080labs/pyforest

    1480
  • Spark的Dockerfile分析

    Spark 容器化的前提是需要 Spark 的镜像文件,那么怎么 build 呢?Spark 官方是提供了 Dockerfile 的,并且也提供了脚本工具,可以...

    runzhliu
  • Mac部署spark2.4.4

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    程序员欣宸

扫码关注云+社区

领取腾讯云代金券