* @Date: 2026-08-07 10:22:47 * @LastEditors: YwxApp * @LastEditTime: 2026-08-20 00:36:36 * @Description: * @FilePath: \ywxapp_dev\ywxapp\model\CommonCard.php * @CustomString: Copyright (c) 2026 YwxApp */ declare(strict_types=1); namespace ywxapp\model; use ywxapp\model\BaseModel; use think\facade\Db; use think\model\concern\SoftDelete; /** * 充值卡密 */ class CommonCard extends BaseModel { use SoftDelete; protected function getOptions(): array { return [ 'strict' => false, 'autoWriteTimestamp' => 'int', 'createTime' => 'create_at', 'updateTime' => 'update_at', 'deleteTime' => 'delete_at', 'defaultSoftDelete' => 0, 'append' => ['target'], 'hidden' => ['create_at', 'update_at', 'delete_at'], 'readonly' => ['id'], ]; } // 卡密状态 const STATUS_UNSOLD = 0; // 未售 const STATUS_SOLD = 1; // 已售 const STATUS_USED = 2; // 已用(已兑换) /** * 运行时自愈:确保 card 主表存在(install.sql 为事实源)。 */ public static function ensureSchema(): void { BaseModel::ensureTableFromInstall(BaseModel::currentPrefix(), 'common_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_wallet')->where('uid', $uid)->find(); if (empty($wallet)) { Db::name('member_wallet')->insert([ 'uid' => $uid, 'balance' => $amount, 'total_recharge' => $amount, 'create_at' => time(), 'update_at' => time(), ]); } else { Db::name('member_wallet') ->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; } }