位置: 文档库 > PHP > PHP商城开发教程:手把手教你实现优惠券功能

PHP商城开发教程:手把手教你实现优惠券功能

汽水味月光2052 上传于 2020-07-20 19:45

《PHP商城开发教程:手把手教你实现优惠券功能》

在电商系统中,优惠券功能是提升用户转化率和复购率的核心模块之一。本文将通过完整的PHP实现流程,从数据库设计到前端交互,逐步讲解如何为商城系统开发一个可复用的优惠券功能。内容涵盖优惠券类型定义、发放逻辑、使用规则校验以及与订单系统的集成。

一、功能需求分析

优惠券系统需支持以下核心功能:

  • 多种优惠券类型:满减券折扣券、无门槛券

  • 发放方式:手动发放、注册赠送、订单完成后赠送

  • 使用限制:有效期、最低消费金额、商品类别限制

  • 状态管理:未使用、已使用、已过期

二、数据库设计

设计三张核心数据表:

1. 优惠券模板表(coupon_templates)

CREATE TABLE `coupon_templates` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL COMMENT '优惠券名称',
  `type` tinyint(1) NOT NULL COMMENT '类型:1满减 2折扣 3无门槛',
  `discount_amount` decimal(10,2) DEFAULT NULL COMMENT '满减金额/折扣比例',
  `min_order_amount` decimal(10,2) DEFAULT NULL COMMENT '最低消费金额',
  `valid_days` int(11) DEFAULT NULL COMMENT '有效期(天)',
  `start_time` datetime DEFAULT NULL COMMENT '生效时间',
  `end_time` datetime DEFAULT NULL COMMENT '失效时间',
  `goods_category_ids` varchar(255) DEFAULT NULL COMMENT '适用商品分类ID',
  `total_count` int(11) NOT NULL COMMENT '发放总量',
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2. 用户优惠券表(user_coupons)

CREATE TABLE `user_coupons` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  `template_id` int(11) NOT NULL COMMENT '模板ID',
  `code` varchar(32) NOT NULL COMMENT '优惠券码',
  `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态:0未使用 1已使用 2已过期',
  `order_id` int(11) DEFAULT NULL COMMENT '关联订单ID',
  `obtained_at` datetime NOT NULL COMMENT '获取时间',
  `expired_at` datetime NOT NULL COMMENT '过期时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3. 优惠券使用记录表(coupon_logs)

CREATE TABLE `coupon_logs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `coupon_id` int(11) NOT NULL,
  `order_id` int(11) NOT NULL,
  `used_amount` decimal(10,2) NOT NULL COMMENT '抵扣金额',
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

三、核心功能实现

1. 优惠券发放服务

创建CouponService类处理发放逻辑:

class CouponService {
    
    public function issueCoupon($userId, $templateId) {
        $template = $this->getTemplateById($templateId);
        if (!$template || $template['remaining_count'] generateCouponCode();
        
        // 计算过期时间
        $expiredAt = $template['valid_days'] > 0 
            ? date('Y-m-d H:i:s', strtotime("+{$template['valid_days']} days"))
            : $template['end_time'];
        
        // 插入用户优惠券记录
        $userCoupon = [
            'user_id' => $userId,
            'template_id' => $templateId,
            'code' => $code,
            'status' => 0,
            'obtained_at' => date('Y-m-d H:i:s'),
            'expired_at' => $expiredAt
        ];
        
        $couponId = Db::name('user_coupons')->insertGetId($userCoupon);
        
        // 更新模板剩余数量
        Db::name('coupon_templates')
            ->where('id', $templateId)
            ->dec('remaining_count')
            ->update();
            
        return $couponId;
    }
    
    private function generateCouponCode() {
        return strtoupper(substr(md5(uniqid()), 0, 8));
    }
}

2. 优惠券使用校验

在订单确认页面需要验证优惠券有效性:

class CouponValidator {
    
    public function validate($couponCode, $userId, $orderAmount) {
        $coupon = Db::name('user_coupons')
            ->alias('uc')
            ->join('coupon_templates ct', 'uc.template_id = ct.id')
            ->where('uc.code', $couponCode)
            ->where('uc.user_id', $userId)
            ->where('uc.status', 0)
            ->where('uc.expired_at >', date('Y-m-d H:i:s'))
            ->find();
            
        if (!$coupon) {
            throw new Exception('优惠券无效或已过期');
        }
        
        // 检查最低消费
        if ($coupon['min_order_amount'] > 0 && 
            $orderAmount 

3. 订单系统集成

在创建订单时应用优惠券:

class OrderService {
    
    public function createOrder($userId, $goodsList, $couponCode = null) {
        // 计算商品总价
        $orderAmount = $this->calculateGoodsTotal($goodsList);
        
        // 验证优惠券
        $coupon = null;
        $discountAmount = 0;
        if ($couponCode) {
            try {
                $couponValidator = new CouponValidator();
                $coupon = $couponValidator->validate($couponCode, $userId, $orderAmount);
                $discountAmount = $couponValidator->calculateDiscount($coupon, $orderAmount);
            } catch (Exception $e) {
                throw new Exception($e->getMessage());
            }
        }
        
        // 最终支付金额
        $payAmount = $orderAmount - $discountAmount;
        
        // 创建订单记录...
        $orderId = $this->insertOrderRecord(/* 参数 */);
        
        // 如果使用了优惠券,更新状态并记录
        if ($coupon) {
            Db::name('user_coupons')
                ->where('id', $coupon['id'])
                ->update([
                    'status' => 1,
                    'order_id' => $orderId,
                    'used_at' => date('Y-m-d H:i:s')
                ]);
                
            Db::name('coupon_logs')->insert([
                'user_id' => $userId,
                'coupon_id' => $coupon['id'],
                'order_id' => $orderId,
                'used_amount' => $discountAmount,
                'created_at' => date('Y-m-d H:i:s')
            ]);
        }
        
        return $orderId;
    }

四、前端交互实现

1. 优惠券列表展示

// AJAX获取用户优惠券
public function getUserCoupons() {
    $userId = session('user_id');
    $coupons = Db::name('user_coupons')
        ->alias('uc')
        ->field('uc.*, ct.name, ct.type, ct.discount_amount, ct.min_order_amount')
        ->join('coupon_templates ct', 'uc.template_id = ct.id')
        ->where('uc.user_id', $userId)
        ->where('uc.status', 0)
        ->where('uc.expired_at >', date('Y-m-d H:i:s'))
        ->select();
        
    return json(['coupons' => $coupons]);
}

2. 优惠券选择组件




五、高级功能扩展

1. 优惠券堆叠使用

修改验证逻辑支持多张优惠券叠加:

public function validateMultipleCoupons($couponCodes, $userId, $orderAmount) {
    $coupons = [];
    $totalDiscount = 0;
    
    foreach ($couponCodes as $code) {
        try {
            $validator = new CouponValidator();
            $coupon = $validator->validate($code, $userId, $orderAmount - $totalDiscount);
            $discount = $validator->calculateDiscount($coupon, $orderAmount - $totalDiscount);
            
            $coupons[] = $coupon;
            $totalDiscount += $discount;
            
            // 防止超额抵扣
            if ($totalDiscount >= $orderAmount) break;
        } catch (Exception $e) {
            // 收集错误信息
        }
    }
    
    return [
        'valid_coupons' => $coupons,
        'total_discount' => $totalDiscount,
        'errors' => /* 错误信息 */
    ];
}

2. 定时任务处理过期优惠券

创建Cron任务每天执行:

// artisan命令方式
public function handle() {
    $expiredCoupons = Db::name('user_coupons')
        ->where('expired_at', 'where('status', 0)
        ->select();
        
    foreach ($expiredCoupons as $coupon) {
        Db::name('user_coupons')
            ->where('id', $coupon['id'])
            ->update(['status' => 2]);
    }
    
    $this->info('处理完成,共过期' . count($expiredCoupons) . '张优惠券');
}

六、安全与性能优化

1. 防刷机制

// 限制单个用户领取次数
public function checkUserLimit($userId, $templateId) {
    $count = Db::name('user_coupons')
        ->where('user_id', $userId)
        ->where('template_id', $templateId)
        ->where('status', '', 2)
        ->count();
        
    $template = Db::name('coupon_templates')->find($templateId);
    if ($count >= $template['user_limit']) {
        throw new Exception('已达到领取上限');
    }
}

2. 数据库索引优化

ALTER TABLE `user_coupons` ADD INDEX `idx_user_status` (`user_id`, `status`);
ALTER TABLE `coupon_templates` ADD INDEX `idx_type_status` (`type`, `status`);

七、测试用例设计

必须覆盖的测试场景:

  • 正常领取和使用流程

  • 过期优惠券自动标记

  • 最低消费不足时的拦截

  • 商品类别限制验证

  • 并发领取时的库存控制

关键词:PHP商城开发、优惠券系统、数据库设计、满减券、折扣券、无门槛券优惠券校验订单集成、定时任务、安全优化

简介:本文详细讲解了PHP商城系统中优惠券功能的完整实现方案,包含数据库设计、核心服务类开发、前后端交互、高级功能扩展及安全优化等内容,适合中高级PHP开发者学习电商系统核心模块开发。

PHP相关