Files

166 lines
5.4 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace ywxapp\model;
use ywxapp\model\BaseModel;
use think\facade\Db;
use think\model\concern\SoftDelete;
/**
* 充值卡密
*/
class Card extends BaseModel
{
use SoftDelete;
protected $name = 'card';
protected $deleteTime = 'delete_at';
protected $defaultSoftDelete = 0;
// 时间戳自动写入
protected $autoWriteTimestamp = true;
protected $createTime = 'create_at';
protected $updateTime = 'update_at';
// 卡密状态
const STATUS_UNSOLD = 0; // 未售
const STATUS_SOLD = 1; // 已售
const STATUS_USED = 2; // 已用(已兑换)
/**
* 运行时自愈:确保 card 主表存在(install.sql 为事实源)。
*/
public static function ensureSchema(): void
{
BaseModel::ensureTableFromInstall(BaseModel::currentPrefix(), 'card');
}
/**
* 初始化:自愈建表,避免远程库缺失 wxapp_card 导致 1146。
*/
protected function initialize()
{
parent::initialize();
self::ensureSchema();
}
/**
* 兑换卡密(会员用卡号+密码充值余额)
*
* @param int $uid 会员ID
* @param string $cardno 卡号
* @param string $password 密码
* @return array ['success'=>bool,'msg'=>string,'data'=>array]
*/
public static function redeem(int $uid, string $cardno, string $password): array
{
if ($uid <= 0 || $cardno === '' || $password === '') {
return ['success' => false, 'msg' => '参数不完整'];
}
$card = self::where('cardno', $cardno)->find();
if (empty($card)) {
return ['success' => false, 'msg' => '卡密不存在'];
}
if ($card->delete_at > 0) {
return ['success' => false, 'msg' => '卡密已失效'];
}
if ((int)$card->status === self::STATUS_USED) {
return ['success' => false, 'msg' => '该卡密已被使用'];
}
// 密码校验(存储若为明文,按需改为 password_verify
if ((string)$card->password !== (string)$password) {
return ['success' => false, 'msg' => '卡号或密码错误'];
}
$amount = (float)$card->amount;
if ($amount <= 0) {
return ['success' => false, 'msg' => '卡密面值异常'];
}
Db::startTrans();
try {
// 1) 卡密标记已用,绑定会员
$card->status = self::STATUS_USED;
$card->use_time = time();
$card->uid = $uid;
$card->save();
// 2) 会员钱包加余额 + 累计充值(无钱包则自动创建)
$wallet = Db::name('member_wallets')->where('uid', $uid)->find();
if (empty($wallet)) {
Db::name('member_wallets')->insert([
'uid' => $uid,
'balance' => $amount,
'total_recharge' => $amount,
'create_at' => time(),
'update_at' => time(),
]);
} else {
Db::name('member_wallets')
->where('uid', $uid)
->inc('balance', $amount)
->inc('total_recharge', $amount)
->update(['update_at' => time()]);
}
// 3) 充值流水
Db::name('member_bill')->insert([
'uid' => $uid,
'type' => 1, // 充值
'amount' => $amount,
'currency' => 1, // 人民币
'channel' => 'card',
'order_no' => 'CARD' . date('YmdHis') . $uid . mt_rand(100, 999),
'status' => 1, // 成功
'description' => '卡密充值:' . $cardno,
'create_at' => time(),
'update_at' => time(),
]);
Db::commit();
return [
'success' => true,
'msg' => '兑换成功,已充值 ¥' . number_format($amount, 2),
'data' => ['amount' => $amount],
];
} catch (\Throwable $e) {
Db::rollback();
throw $e;
}
}
/**
* 批量生成卡密
*
* @param int $count 生成数量
* @param float $amount 面值
* @param string $prefix 卡号前缀(如 YX
* @return array 生成的卡号列表
*/
public static function generateBatch(int $count, float $amount, string $prefix = ''): array
{
$count = max(1, min(200, $count)); // 单次上限保护
$batchNo = date('YmdHis') . mt_rand(1000, 9999);
$list = [];
$rows = [];
for ($i = 0; $i < $count; $i++) {
$cardno = ($prefix ?: 'YX') . strtoupper(substr(md5(uniqid((string)mt_rand(), true)), 0, 16));
$password = strtoupper(substr(md5(uniqid((string)mt_rand(), true)), 0, 8));
$list[] = ['cardno' => $cardno, 'password' => $password];
$rows[] = [
'cardno' => $cardno,
'password' => $password,
'amount' => $amount,
'status' => self::STATUS_UNSOLD,
'batch_no' => $batchNo,
'create_at' => time(),
'update_at' => time(),
];
}
self::insertAll($rows);
return $list;
}
}