AdminSidebarInit($params['admin_left_menu']); break; // 订单支付成功处理 case 'plugins_service_order_pay_success_handle_end': BaseService::log('Hook::handle triggered', ['order_id' => $params['order_id'] ?? $params['business_id'] ?? 'unknown'], 'info'); $ret = TicketService::onOrderPaid($params); BaseService::log('Hook::handle result', ['ret' => $ret], 'info'); break; case 'plugins_service_order_detail_page_info': // C端订单详情页注入票夹入口 $ret = $this->InjectTicketCard($params); break; case 'plugins_view_user_various_bottom': // C端用户中心底部挂载票夹入口 $ret = $this->InjectWalletLink($params); break; case 'plugins_service_buy_order_insert_begin': // C端下单前校验:距离开场不足5分钟禁止购买 $ret = $this->BuyCheck($params); break; case 'plugins_service_order_delete_success': // 如果有删除拦截等 break; } return $ret; } } public function AdminSidebarInit(&$params) { $params[] = [ 'id' => 'plugins-vr_ticket', 'name' => 'VR票务', 'title' => 'VR票务', 'icon' => 'am-icon-ticket', 'control' => 'admin', 'action' => 'index', 'is_show' => 1, 'power' => 'vr_ticket-admin', 'url' => PluginsAdminUrl('vr_ticket', 'admin', 'index'), 'items' => [ [ 'id' => 'plugins-vr_ticket-venue', 'name' => '场馆配置', 'title' => '场馆配置', 'is_show' => 1, 'control' => 'admin', 'action' => 'VenueList', 'url' => PluginsAdminUrl('vr_ticket', 'admin', 'VenueList'), 'power' => 'vr_ticket-venueList', ], [ 'id' => 'plugins-vr_ticket-ticket', 'name' => '电子票', 'title' => '电子票', 'is_show' => 1, 'control' => 'admin', 'action' => 'TicketList', 'url' => PluginsAdminUrl('vr_ticket', 'admin', 'TicketList'), 'power' => 'vr_ticket-ticketList', ], [ 'id' => 'plugins-vr_ticket-ticketverify', 'name' => '扫码核销', 'title' => '扫码核销', 'is_show' => 1, 'control' => 'admin', 'action' => 'TicketVerify', 'url' => PluginsAdminUrl('vr_ticket', 'admin', 'TicketVerify'), 'power' => 'vr_ticket-ticketVerify', ], [ 'id' => 'plugins-vr_ticket-verifier', 'name' => '核销员', 'title' => '核销员', 'is_show' => 1, 'control' => 'admin', 'action' => 'VerifierList', 'url' => PluginsAdminUrl('vr_ticket', 'admin', 'VerifierList'), 'power' => 'vr_ticket-verifierList', ], [ 'id' => 'plugins-vr_ticket-varification', 'name' => '核销记录', 'title' => '核销记录', 'is_show' => 1, 'control' => 'admin', 'action' => 'VerificationList', 'url' => PluginsAdminUrl('vr_ticket', 'admin', 'VerificationList'), 'power' => 'vr_ticket-verificationList', ], [ 'id' => 'plugins-vr_ticket-setup', 'name' => '插件设置', 'title' => '插件设置', 'is_show' => 1, 'control' => 'admin', 'action' => 'Setup', 'url' => PluginsAdminUrl('vr_ticket', 'admin', 'Setup'), 'power' => 'vr_ticket-setup', ] ] ]; } /** * C端订单详情页注入票卡片 */ public function InjectTicketCard(&$params) { $order = $params['order'] ?? []; if (empty($order) || ($order['pay_status'] ?? 0) != 1) { return; } // 获取当前登录用户(ShopXO 标准方式) $user = \app\service\UserService::LoginUserInfo(); $userId = empty($user) ? null : $user['id']; if (empty($userId)) { return; } $tickets = \think\facade\Db::name('vr_tickets') ->where('order_id', $order['id']) ->select() ->toArray(); if (empty($tickets)) { return; } $token = session('user_token') ?: ''; $hostUrl = \think\facade\Config::get('shopxo.host_url'); $ticketCardsHtml = ''; foreach ($tickets as $ticket) { $shortCode = \app\plugins\vr_ticket\service\BaseService::shortCodeEncode($ticket['goods_id'], $ticket['id']); $statusMap = [0 => ['text' => '未核销', 'class' => 'unverified'], 1 => ['text' => '已核销', 'class' => 'verified'], 2 => ['text' => '已退款', 'class' => 'refunded']]; $status = $statusMap[$ticket['verify_status']] ?? $statusMap[0]; $ticketCardsHtml .= '
' . '
' . '
电子票
' . '
' . $status['text'] . '
' . '
' . '
' . '
💺' . htmlspecialchars($ticket['seat_info'] ?? '') . '
' . '
👤' . htmlspecialchars($ticket['real_name'] ?? '') . '
' . '
' . '' . '
'; } $style = ''; $ticketHtml = '
' . '
📋 我的电子票
' . $ticketCardsHtml . '
'; $params['page_data']['ticket_section'] = $ticketHtml; $params['page_data']['ticket_css'] = $style; // JS $js = ''; $params['page_data']['ticket_js'] = $js; } /** * 在用户中心底部挂载票夹入口链接 */ /** * C端下单前校验:票务商品数据完整性 + 停售时间校验 * * 两层校验(仅针对票务商品): * 1. 数据完整性:batch_number_expire 必须 > 0(演出日期必填),否则拒绝下单 * 2. 时效性:batch_expire_ts(演出前5分钟截止),已过期则拒绝下单 * * @param array $params 钩子参数 ['hook_name' => ..., 'data' => ..., 'order' => ..., 'goods' => ...] * @return array ['code' => 0|非0, 'msg' => ...] */ public function BuyCheck(&$params) { $goodsItems = $params['goods'] ?? []; if (empty($goodsItems)) { return ['code' => 0, 'msg' => '']; } // ────────────────────────────────────────────────────── // Step 1: 批量查询票务商品标识 // 提取所有 goods_id,一次性查询 vr_goods_config 非空的商品 // 构建 ticketGoodsMap {goods_id => has_config} // ────────────────────────────────────────────────────── $goodsIds = []; foreach ($goodsItems as $item) { $gid = intval($item['goods_id'] ?? 0); if ($gid > 0) { $goodsIds[] = $gid; } } $goodsIds = array_unique($goodsIds); $ticketGoodsMap = []; // goods_id => has_config (bool) if (!empty($goodsIds)) { $ticketGoods = Db::name('Goods') ->where('id', 'in', $goodsIds) ->where('vr_goods_config', '<>', '') ->field('id, batch_number_expire') ->select() ->toArray(); foreach ($ticketGoods as $tg) { $ticketGoodsMap[$tg['id']] = [ 'has_config' => true, 'batch_number_expire' => intval($tg['batch_number_expire'] ?? 0), ]; } } // ────────────────────────────────────────────────────── // Step 2: 逐商品校验 // ────────────────────────────────────────────────────── $now = time(); foreach ($goodsItems as $item) { $gid = intval($item['goods_id'] ?? 0); $isTicketGood = isset($ticketGoodsMap[$gid]) && $ticketGoodsMap[$gid]['has_config']; // 非票务商品:跳过 if (!$isTicketGood) { continue; } // --- 校验 1: batch_number_expire 数据完整性 --- // 优先用 DB 查询结果(最新);其次用订单 item 中的值 $dbBatchExpire = $ticketGoodsMap[$gid]['batch_number_expire'] ?? 0; if ($dbBatchExpire <= 0) { $itemTitle = $item['title'] ?? ('商品#' . $gid); return [ 'code' => -1, 'msg' => '「' . $itemTitle . '」未设置演出日期,暂时无法购买', ]; } // --- 校验 2: batch_expire_ts 停售时间 --- // 从 SKU extends 读取(SeatSkuService::BatchGenerate 已写入) $extends = $item['extends'] ?? []; if (empty($extends) || !is_array($extends)) { if (!empty($item['extends']) && is_string($item['extends'])) { $extends = json_decode($item['extends'], true); } if (empty($extends) || !is_array($extends)) { continue; // 无 extends 数据,跳过停售校验(放过) } } if (!isset($extends['batch_expire_ts'])) { continue; // 未设置停售时间,跳过 } $batchExpireTs = intval($extends['batch_expire_ts']); if ($batchExpireTs <= 0) { continue; } if ($now >= $batchExpireTs) { $sessionInfo = ''; if (!empty($extends['session_start']) && !empty($extends['session_end'])) { $sessionInfo = '(' . $extends['session_start'] . ' - ' . $extends['session_end'] . ')'; } return [ 'code' => -1, 'msg' => '该场次' . $sessionInfo . '距开场已不足5分钟,已停止售票,请选择其他场次', ]; } } return ['code' => 0, 'msg' => '']; } public function InjectWalletLink(&$params) { $hostUrl = \think\facade\Config::get('shopxo.host_url'); // 票夹入口 HTML - 直接返回 HTML 字符串 // 正确的插件路由格式:?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=index&pluginsaction=wallet $walletLink = '
' . '' . '🎫 我的电子票' . '
'; return $walletLink; } } ?>