通用系統設計之優惠卷

clipboard.png

前言

本應該繼續連載手擼框架系列文章的。但最近有一個需求 -> 優惠卷,之前很多朋友讓我出一篇優惠卷相關的文章。這不,本章應了大夥的願。開始我自己的表演 🔥🔥

額,這裏還要插一句,有很多新人感覺在使用框架的過程中根本用不到PHP的很多概念,例如abstract,final 部分人感覺protected,private 都沒有太大用處。更別提interface在框架中的使用了,感覺好無用處的舉爪~

策略模式

優惠卷的存在到消亡至少要經歷三個步驟(創建->使用->失效),以下爲優惠卷完整生命週期圖,

clipboard.png

優惠卷有幾百種幾千種的優惠(騙人)方式(姿勢),結合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;

致謝

希望每篇文章並不是僅僅講一件問題,我會把問題的擴展思想一併告訴大家,希望可以幫助到你。謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章