LikeShop 虚拟商品与电子卡券二开:卡券发放、有效期管理与核销流程

一、原生商品类型与虚拟商品的缺口

1.1 原生商品体系的类型设计

LikeShop 的 ls_goods 表通过 goods_type 字段区分商品类型,原生支持实物商品和虚拟商品两类;虚拟商品不参与物流发货,订单完成后直接进入“已完成”状态,无需填写收货地址和物流单号;但原生的虚拟商品仅支持“一次性交付”模式——用户购买后系统交付一个固定的虚拟内容(如课程链接、网盘地址),没有卡券化、兑换码化、有效期管理和核销记录的能力。

1.2 电子卡券场景的四个核心缺口

缺口一:无卡券实体与库存管理。原生的虚拟商品交付的是一个静态内容,不支持“一个商品对应多张卡券”的模式;商家无法为一个虚拟商品批量生成1000张兑换码,每码独立核销、独立记录有效期。

缺口二:无有效期管控。原生虚拟商品没有有效期概念;电子卡券场景下,每张卡券需要独立的生效时间和过期时间,过期后自动失效且不可核销;原生系统缺乏有效期字段和过期自动失效的定时任务。

缺口三:无核销流程与记录。电子卡券的核心是“核销”——用户持券到店或在线兑换,系统记录核销时间、核销人、核销方式;原生的虚拟商品订单只有“已支付/已完成”状态,没有“已核销/未核销”的区分,也没有核销日志。

1.3 二开策略定位

遵循 LikeShop 的“扩展优先”原则,虚拟商品与卡券模块采用以下策略:在 ls_goods 的 goods_type 枚举中新增“电子卡券”类型(值为3),卡券的实体数据、有效期、核销记录通过独立的数据表管理;订单支付成功后,通过事件监听器触发卡券的发放和绑定;核销逻辑封装在独立的 CardVerifyService 中,不修改原生的订单状态机。

二、数据库扩展设计

2.1 电子卡券商品配置表

php
CREATE TABLE `ls_goods_card_config` (
 `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
 `goods_id` int UNSIGNED NOT NULL COMMENT '商品ID',
 `card_type` tinyint NOT NULL DEFAULT 1 COMMENT '卡券类型:1-兑换码,2-电子券,3-会员权益卡',
 `card_name` varchar(100) NOT NULL COMMENT '卡券名称',
 `card_desc` varchar(500) NOT NULL DEFAULT '' COMMENT '卡券说明',
 `valid_type` tinyint NOT NULL DEFAULT 1 COMMENT '有效期类型:1-固定日期,2-领取后N天,3-永久有效',
 `valid_start_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '固定有效期开始时间',
 `valid_end_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '固定有效期结束时间',
`valid_days` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '领取后有效天数',
`code_prefix` varchar(10) NOT NULL DEFAULT '' COMMENT '兑换码前缀',
`code_length` tinyint NOT NULL DEFAULT 12 COMMENT '兑换码长度',
`stock_type` tinyint NOT NULL DEFAULT 1 COMMENT '库存模式:1-预生成卡券池,2-购买时动态生成',
`card_stock` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '卡券库存总量',
`sold_count` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '已售出数量',
`verify_type` tinyint NOT NULL DEFAULT 1 COMMENT '核销方式:1-线上兑换,2-线下扫码核销,3-两者都支持',
`verify_limit` int UNSIGNED NOT NULL DEFAULT 1 COMMENT '每张卡券可核销次数',
`exchange_goods_ids` varchar(255) NOT NULL DEFAULT '' COMMENT '兑换商品ID,逗号分隔(线上兑换时使用)',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
`create_time` int UNSIGNED NOT NULL DEFAULT 0,
`update_time` int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_goods_id` (`goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='电子卡券商品配置表';

valid_type 支持三种有效期模式:固定日期(如“2026年12月31日前有效”)、领取后N天(如“领取后30天内有效”)、永久有效;stock_type 区分预生成和动态生成两种库存模式。

2.2 卡券实体表

这是卡券的核心数据表,每张卡券对应一条记录:

sql
CREATE TABLE `ls_card_instance` (
 `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
 `card_config_id` int UNSIGNED NOT NULL COMMENT '卡券配置ID',
 `goods_id` int UNSIGNED NOT NULL COMMENT '商品ID',
 `card_code` varchar(32) NOT NULL COMMENT '卡券兑换码(唯一)',
 `card_secret` varchar(32) NOT NULL DEFAULT '' COMMENT '卡券密钥(二次验证用)',
 `batch_no` varchar(32) NOT NULL DEFAULT '' COMMENT '生成批次号',
 `user_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '持有用户ID,0为未售出',
 `order_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '关联订单ID',
 `order_sn` varchar(32) NOT NULL DEFAULT '' COMMENT '关联订单号',
 `valid_start_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '生效时间',
 `valid_end_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '过期时间,0为永久有效',
 `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1-未使用,2-已使用,3-已过期,4-已作废,5-已冻结',
  `verify_count` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '已核销次数',
  `max_verify_count` int UNSIGNED NOT NULL DEFAULT 1 COMMENT '最大核销次数',
  `first_verify_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '首次核销时间',
  `last_verify_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '最后核销时间',
  `create_time` int UNSIGNED NOT NULL DEFAULT 0,
  `update_time` int UNSIGNED NOT NULL DEFAULT 0,
 PRIMARY KEY (`id`),
 UNIQUE KEY `uk_card_code` (`card_code`),
 KEY `idx_user_id` (`user_id`),
 KEY `idx_order_id` (`order_id`),
 KEY `idx_status_valid` (`status`, `valid_end_time`),
 KEY `idx_batch_no` (`batch_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='卡券实体表';

card_code 是用户看到的兑换码,全局唯一;card_secret 是二次验证的密钥(可选),用于放伪造;status 的枚举覆盖卡券的完整生命周期:未使用、已使用、已过期、已作废、已冻结。

2.3 核销记录表

sql
CREATE TABLE `ls_card_verify_log` (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
  `card_instance_id` int UNSIGNED NOT NULL COMMENT '卡券实例ID',
  `card_code` varchar(32) NOT NULL COMMENT '卡券兑换码',
  `user_id` int UNSIGNED NOT NULL COMMENT '核销用户ID',
  `verify_type` tinyint NOT NULL DEFAULT 1 COMMENT '核销方式:1-线上兑换,2-线下扫码,3-后台手动核销',
  `operator_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '操作人ID(线下核销/后台核销时记录)',
  `operator_type` tinyint NOT NULL DEFAULT 1 COMMENT '操作人类型:1-用户,2-核销员,3-平台管理员',
  `exchange_goods_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '兑换的商品ID(线上兑换时)',
 `exchange_order_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '兑换生成的订单ID',
 `verify_result` tinyint NOT NULL DEFAULT 1 COMMENT '核销结果:1-成功,2-失败',
 `fail_reason` varchar(255) NOT NULL DEFAULT '' COMMENT '失败原因',
 `verify_ip` varchar(45) NOT NULL DEFAULT '',
 `create_time` int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_card_instance` (`card_instance_id`),
KEY `idx_card_code` (`card_code`),
KEY `idx_user_id` (`user_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='卡券核销记录表';
三、卡券批量生成方案

3.1 预生成模式

预生成模式下,商家在后台配置好卡券数量(如1000张),系统一次性批量生成1000条 ls_card_instance 记录,user_id 为 0(未售出);用户购买后,系统从卡券池中取出未售出的卡券绑定到用户。

批量生成的核心是兑换码的唯一性;采用“前缀 + 随机串”的生成策略,生成后通过数据库唯一索引做最终去重:

php
class CardGenerateService
{
/**
 * 批量生成卡券
 */
public function batchGenerate($configId, $count, $batchNo = '')
{
    $config = GoodsCardConfigModel::find($configId);
    if (!$config) {
        throw new \Exception('卡券配置不存在');
    }

    $batchNo = $batchNo ?: date('YmdHis') . mt_rand(100, 999);
    $prefix = $config['code_prefix'];
    $length = max(8, min(32, $config['code_length']));
    $randomLength = $length - strlen($prefix);

    // 计算有效期时间范围
    [$validStart, $validEnd] = $this->calcValidTime($config);

    $generatedCodes = [];
    $insertData = [];
    $batchSize = 500;

    for ($i = 0; $i < $count; $i++) {
        // 生成唯一兑换码
        $code = $this->generateUniqueCode($prefix, $randomLength, $generatedCodes);
        $generatedCodes[] = $code;

        $insertData[] = [
            'card_config_id'  => $configId,
            'goods_id'        => $config['goods_id'],
            'card_code'       => $code,
            'batch_no'        => $batchNo,
            'user_id'         => 0,
            'valid_start_time'=> $validStart,
            'valid_end_time'  => $validEnd,
            'status'          => 1,
            'max_verify_count'=> $config['verify_limit'],
            'create_time'     => time(),
        ];

        // 分批写入
        if (count($insertData) >= $batchSize) {
            Db::name('card_instance')->insertAll($insertData);
            $insertData = [];
        }
    }

    // 写入剩余数据
    if (!empty($insertData)) {
        Db::name('card_instance')->insertAll($insertData);
    }

    // 更新卡券库存
    GoodsCardConfigModel::where('id', $configId)
        ->inc('card_stock', $count)->update();

    return ['batch_no' => $batchNo, 'generated_count' => $count];
}

/**
 * 生成唯一兑换码
 */
private function generateUniqueCode($prefix, $length, $excludeCodes = [])
{
    $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // 去掉易混淆的0/O/1/I
    $maxAttempts = 100;

    for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
        $random = '';
        for ($j = 0; $j < $length; $j++) {
            $random .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        $code = $prefix . $random;

        // 检查批次内是否重复
        if (in_array($code, $excludeCodes)) continue;

        // 检查数据库是否已存在
        $exists = CardInstanceModel::where('card_code', $code)->count();
        if ($exists == 0) {
            return $code;
        }
    }

    throw new \Exception('兑换码生成失败,请重试');
}

/**
 * 计算卡券有效期时间范围
 */
private function calcValidTime($config)
{
    switch ($config['valid_type']) {
        case 1: // 固定日期
            return [$config['valid_start_time'], $config['valid_end_time']];
        case 2: // 领取后N天
            return [0, 0]; // 在发放时动态计算
        case 3: // 永久有效
            return [0, 0];
        default:
            return [0, 0];
       }
     }
  }

3.2 动态生成模式

动态生成模式下,卡券不预先创建,而是在用户购买时实时生成;这种模式适合“按需生产”的场景,避免大量卡券闲置:

 php
 /**
* 购买时动态生成卡券
*/
public function generateOnPurchase($configId, $userId, $orderId, $orderSn)
{
$config = GoodsCardConfigModel::find($configId);

// 校验库存
if ($config['sold_count'] >= $config['card_stock']) {
    throw new \Exception('卡券库存不足');
}

Db::startTrans();
try {
    $code = $this->generateUniqueCode($config['code_prefix'],
        $config['code_length'] - strlen($config['code_prefix']));

    // 计算有效期
    $validStart = time();
    $validEnd = 0;
    if ($config['valid_type'] == 1) {
        $validStart = $config['valid_start_time'];
        $validEnd = $config['valid_end_time'];
    } elseif ($config['valid_type'] == 2) {
        $validEnd = time() + $config['valid_days'] * 86400;
    }

    CardInstanceModel::create([
        'card_config_id'  => $configId,
        'goods_id'        => $config['goods_id'],
        'card_code'       => $code,
        'user_id'         => $userId,
        'order_id'        => $orderId,
        'order_sn'        => $orderSn,
        'valid_start_time'=> $validStart,
        'valid_end_time'  => $validEnd,
        'status'          => 1,
        'max_verify_count'=> $config['verify_limit'],
        'create_time'     => time(),
    ]);

    GoodsCardConfigModel::where('id', $configId)
        ->inc('sold_count', 1)->update();

    Db::commit();
    return $code;
} catch (\Exception $e) {
    Db::rollback();
    throw $e;
  }
}

3.3 预生成卡券的发放

预生成模式下,订单支付成功后从卡券池中绑定卡券到用户:

php
class CardDispatchService
{
/**
 * 订单支付成功后发放卡券
 */
public function dispatch($orderId, $goodsId, $num)
{
    $config = GoodsCardConfigModel::where('goods_id', $goodsId)
        ->where('status', 1)->find();
    if (!$config) return;

    $order = OrderModel::find($orderId);

    for ($i = 0; $i < $num; $i++) {
        // 取出一张未售出的卡券(行锁防止并发)
        $card = CardInstanceModel::where('card_config_id', $config['id'])
            ->where('user_id', 0)
            ->where('status', 1)
            ->lock(true)
            ->find();

        if (!$card) {
            throw new \Exception('卡券库存不足');
        }

        // 计算有效期
        $validStart = $card['valid_start_time'];
        $validEnd = $card['valid_end_time'];
        if ($config['valid_type'] == 2) {
            $validStart = time();
            $validEnd = time() + $config['valid_days'] * 86400;
        }

        // 绑定到用户
        CardInstanceModel::where('id', $card['id'])->update([
            'user_id'         => $order['user_id'],
            'order_id'        => $orderId,
            'order_sn'        => $order['order_sn'],
            'valid_start_time'=> $validStart,
            'valid_end_time'  => $validEnd,
            'update_time'     => time(),
        ]);
    }

    GoodsCardConfigModel::where('id', $config['id'])
        ->inc('sold_count', $num)->update();
   }
}
四、有效期管控与过期自动失效

4.1 有效期校验

卡券在核销前需要校验有效期:

php
public function checkCardValid($cardCode)
{
$card = CardInstanceModel::where('card_code', $cardCode)->find();
if (!$card) {
    throw new \Exception('卡券不存在');
}

if ($card['status'] == 3) {
    throw new \Exception('卡券已过期');
}
if ($card['status'] == 4) {
    throw new \Exception('卡券已作废');
}
if ($card['status'] == 2) {
    throw new \Exception('卡券已使用');
}
if ($card['status'] == 5) {
    throw new \Exception('卡券已冻结');
}

$now = time();
if ($card['valid_start_time'] > 0 && $now < $card['valid_start_time']) {
    throw new \Exception('卡券尚未生效');
}
if ($card['valid_end_time'] > 0 && $now > $card['valid_end_time']) {
    throw new \Exception('卡券已过期');
}

return $card;
} 

4.2 过期自动失效定时任务

通过定时任务每日扫描已过期的卡券,将其状态更新为“已过期”:

php
class CardExpireCrontab
{
/**
 * 卡券过期自动失效(每日凌晨执行)
 */
public function expireCards()
{
    $now = time();
    $batchSize = 1000;
    $totalExpired = 0;

    while (true) {
        // 查询已过期但状态仍为未使用的卡券
        $expiredCards = CardInstanceModel::where('status', 1)
            ->where('valid_end_time', '>', 0)
            ->where('valid_end_time', '<=', $now)
            ->limit($batchSize)
            ->column('id');

        if (empty($expiredCards)) break;

        // 批量更新状态
        CardInstanceModel::whereIn('id', $expiredCards)
            ->update(['status' => 3, 'update_time' => $now]);

        $totalExpired += count($expiredCards);

        // 记录日志
        CardExpireLogModel::create([
            'expired_count' => count($expiredCards),
            'expire_date'   => date('Y-m-d'),
            'create_time'   => $now,
        ]);

        // 发送过期提醒(可选)
        $this->sendExpireNotice($expiredCards);
    }

    return $totalExpired;
}

/**
 * 过期前提醒(提前7天和1天)
 */
public function remindBeforeExpire()
{
    $now = time();

    // 7天后过期的卡券
    $cards7 = CardInstanceModel::where('status', 1)
        ->where('valid_end_time', '>', $now + 6 * 86400)
        ->where('valid_end_time', '<=', $now + 7 * 86400)
        ->select();

    foreach ($cards7 as $card) {
        MessageService::send($card['user_id'], '卡券即将过期',
            "您的卡券「{$card['card_code']}」将于7天后过期,请尽快使用。");
    }

    // 1天后过期的卡券
    $cards1 = CardInstanceModel::where('status', 1)
        ->where('valid_end_time', '>', $now)
        ->where('valid_end_time', '<=', $now + 86400)
        ->select();

    foreach ($cards1 as $card) {
        MessageService::send($card['user_id'], '卡券即将过期',
            "您的卡券「{$card['card_code']}」将于明天过期,请尽快使用。");
    }
  }
}
五、核销流程实现

5.1 线上兑换核销

用户持卡券在线兑换指定商品;核销时校验卡券有效性、兑换商品权限,生成兑换订单:

php
class CardVerifyService
{
 /**
 * 线上兑换核销
 */
public function onlineExchange($userId, $cardCode, $goodsId, $itemId = 0)
{
    // 1. 校验卡券有效性
    $card = $this->checkCardValid($cardCode);
    if ($card['user_id'] != $userId) {
        throw new \Exception('该卡券不属于您');
    }

    // 2. 校验兑换商品权限
    $config = GoodsCardConfigModel::find($card['card_config_id']);
    if (!empty($config['exchange_goods_ids'])) {
        $allowGoods = explode(',', $config['exchange_goods_ids']);
        if (!in_array($goodsId, $allowGoods)) {
            throw new \Exception('该卡券不支持兑换此商品');
        }
    }

    // 3. 校验核销次数
    if ($card['verify_count'] >= $card['max_verify_count']) {
        throw new \Exception('该卡券核销次数已用完');
    }

    Db::startTrans();
    try {
        // 4. 锁定卡券
        $card = CardInstanceModel::lock(true)->find($card['id']);

        // 5. 更新卡券状态
        $newVerifyCount = $card['verify_count'] + 1;
        $newStatus = ($newVerifyCount >= $card['max_verify_count']) ? 2 : 1;

        CardInstanceModel::where('id', $card['id'])->update([
            'status'          => $newStatus,
            'verify_count'    => $newVerifyCount,
            'first_verify_time'=> $card['first_verify_time'] ?: time(),
            'last_verify_time' => time(),
            'update_time'      => time(),
        ]);

        // 6. 生成兑换订单(0元订单)
        $orderSn = generateSn('CE');
        $orderId = OrderModel::create([
            'order_sn'     => $orderSn,
            'user_id'      => $userId,
            'order_type'   => 8, // 8-卡券兑换订单
            'order_amount' => 0,
            'goods_amount' => 0,
            'order_status' => 0,
            'pay_status'   => 1,
            'create_time'  => time(),
        ])->id;

        // 7. 写入核销记录
        CardVerifyLogModel::create([
            'card_instance_id' => $card['id'],
            'card_code'        => $cardCode,
            'user_id'          => $userId,
            'verify_type'      => 1,
            'operator_type'    => 1,
            'exchange_goods_id'=> $goodsId,
            'exchange_order_id'=> $orderId,
            'verify_result'    => 1,
            'verify_ip'        => request()->ip(),
            'create_time'      => time(),
        ]);

        Db::commit();
        return ['order_id' => $orderId, 'order_sn' => $orderSn];
    } catch (\Exception $e) {
        Db::rollback();
        throw $e;
    }
  }
}

5.2 线下扫码核销

线下核销员通过扫码或输入兑换码完成核销;核销员需要先登录核销端,核销时记录操作人:

php
/**
* 线下扫码核销
*/
public function offlineVerify($operatorId, $cardCode)
{
// 1. 校验操作人是否为核销员
$isVerifier = CardVerifierModel::where('user_id', $operatorId)
    ->where('status', 1)->count();
if (!$isVerifier) {
    throw new \Exception('您不是核销员,无权核销');
}

// 2. 校验卡券有效性
$card = $this->checkCardValid($cardCode);

// 3. 校验核销次数
if ($card['verify_count'] >= $card['max_verify_count']) {
    throw new \Exception('该卡券核销次数已用完');
}

Db::startTrans();
try {
    $card = CardInstanceModel::lock(true)->find($card['id']);

    $newVerifyCount = $card['verify_count'] + 1;
    $newStatus = ($newVerifyCount >= $card['max_verify_count']) ? 2 : 1;

    CardInstanceModel::where('id', $card['id'])->update([
        'status'          => $newStatus,
        'verify_count'    => $newVerifyCount,
        'first_verify_time'=> $card['first_verify_time'] ?: time(),
        'last_verify_time' => time(),
        'update_time'      => time(),
    ]);

    CardVerifyLogModel::create([
        'card_instance_id' => $card['id'],
        'card_code'        => $cardCode,
        'user_id'          => $card['user_id'],
        'verify_type'      => 2,
        'operator_id'      => $operatorId,
        'operator_type'    => 2,
        'verify_result'    => 1,
        'verify_ip'        => request()->ip(),
        'create_time'      => time(),
    ]);

    Db::commit();
    return ['card_code' => $cardCode, 'verify_time' => date('Y-m-d H:i:s')];
} catch (\Exception $e) {
    Db::rollback();
    throw $e;
  }
}

5.3 核销失败的记录

核销失败时同样需要记录日志,便于排查问题;失败原因包括:卡券不存在、已过期、已使用、核销员无权、兑换商品不匹配等;失败记录写入 ls_card_verify_log,verify_result 设为 2,fall_reason 记录具体原因。

六、后台配置与开发注意事项

6.1 后台配置清单

在“商品管理 → 电子卡券”中增加配置页面:

配置项类型说明
关联商品商品选择器选择商品类型为“电子卡券”的商品
卡券类型单选兑换码 / 电子券 / 会员权益卡
有效期类型单选固定日期 / 领取后N天 / 永久有效
兑换码前缀文本输入如“VIP”
兑换码长度数字输入8-32位
库存模式单选预生成卡券池 / 购买时动态生成
卡券数量数字输入预生成模式下的批量生成数量
核销方式多选线上兑换 / 线下扫码 / 后台核销
核销次数数字输入每张卡券可核销次数
兑换商品商品多选线上兑换时可选的商品范围

6.2 开发注意事项

兑换码的唯一性与防猜测。兑换码使用去除了易混淆字符的字符集(去掉0/O/1/l),生成后通过数据库唯一索引做最终去重;兑换码长度建议不低于12位,防止被暴力枚举;对于高价值卡券,建议增加 card_secret 二次验证密钥。

并发核销的防重。同一张卡券可能在多端同时被核销(如用户线上兑换的同时,线下核销员也在扫码);核销时必须使用lock(true) 行锁,并在锁内重新校验核销次数,防止并发核销导致超次。

过期卡券的退款处理。卡券过期后状态变为“已过期”,但如果用户申请退款,需要校验卡券是否已被核销;已核销的卡券不支持退款,未核销但已过期的卡券是否可退由商家配置决定。

兼容性。新增字段全部设置默认值,ls_goods 表的 goods_type 枚举新增值3(电子卡券),不影响已有的实物商品和虚拟商品;卡券模块作为独立的后台模块开发,不修改原生的商品发布和订单逻辑;卡券订单的 order_type 使用8(卡券兑换订单),与原生订单类型区分。

0
0
0
0
评论
未登录
暂无评论