通用系统设计之优惠卷

25次阅读

共计 3055 个字符,预计需要花费 8 分钟才能阅读完成。

前言
本应该继续连载手撸框架系列文章的。但最近有一个需求 -> 优惠卷,之前很多朋友让我出一篇优惠卷相关的文章。这不,本章应了大伙的愿。开始我自己的表演 ????????
额, 这里还要插一句,有很多新人感觉在使用框架的过程中根本用不到 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