vr-shopxo-plugin/shopxo/app/plugins/vr_ticket/Hook.php

269 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace app\plugins\vr_ticket;
use app\plugins\vr_ticket\service\TicketService;
class Hook
{
public function handle($params = [])
{
if(!empty($params['hook_name']))
{
$ret = '';
switch($params['hook_name'])
{
// 后台左侧菜单钩子
case 'plugins_service_admin_menu_data':
$this->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_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'),
'item' => [
[
'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-seat',
'name' => '座位模板',
'title' => '座位模板',
'is_show' => 1,
'control' => 'admin',
'action' => 'SeatTemplateList',
'url' => PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateList'),
'power' => 'vr_ticket-seatTemplateList',
],
[
'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-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 .= '<div class="vr-ticket-card" data-ticket-id="' . $ticket['id'] . '">' .
'<div class="vr-ticket-card-header">' .
'<div class="vr-ticket-goods-title">电子票</div>' .
'<div class="vr-ticket-status ' . $status['class'] . '">' . $status['text'] . '</div>' .
'</div>' .
'<div class="vr-ticket-info">' .
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">💺</span><span>' . htmlspecialchars($ticket['seat_info'] ?? '') . '</span></div>' .
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">👤</span><span>' . htmlspecialchars($ticket['real_name'] ?? '') . '</span></div>' .
'</div>' .
'<div class="vr-ticket-footer">' .
'<div class="vr-ticket-short-code">短码: ' . htmlspecialchars($shortCode) . '</div>' .
'<a href="javascript:;" class="vr-ticket-view-btn" onclick="VrTicketWallet.viewTicket(' . $ticket['id'] . ')">查看票码 →</a>' .
'</div>' .
'</div>';
}
$style = '<style>
.vr-ticket-card { background: #fff; border-radius: 12px; padding: 16px; margin: 12px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.06); cursor: pointer; }
.vr-ticket-card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.12); }
.vr-ticket-card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
.vr-ticket-goods-title { font-size: 16px; font-weight: 600; color: #333; }
.vr-ticket-status { font-size: 12px; padding: 2px 8px; border-radius: 4px; font-weight: 500; }
.vr-ticket-status.unverified { background: #e6f7ff; color: #1890ff; }
.vr-ticket-status.verified { background: #f6ffed; color: #52c41a; }
.vr-ticket-status.refunded { background: #fff1f0; color: #ff4d4f; }
.vr-ticket-info { font-size: 13px; color: #666; line-height: 1.6; }
.vr-ticket-info-row { display: flex; align-items: center; margin-bottom: 4px; }
.vr-ticket-info-icon { width: 16px; color: #999; margin-right: 6px; }
.vr-ticket-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; padding-top: 12px; border-top: 1px solid #f0f0f0; }
.vr-ticket-short-code { font-size: 14px; font-family: "Courier New", monospace; color: #333; font-weight: 600; letter-spacing: 1px; }
.vr-ticket-view-btn { font-size: 13px; color: #1890ff; text-decoration: none; }
.vr-ticket-view-btn:hover { text-decoration: underline; }
</style>';
$ticketHtml = '<div class="vr-order-ticket-section">' .
'<div style="font-size:16px;font-weight:600;margin-bottom:12px;">📋 我的电子票</div>' .
$ticketCardsHtml .
'</div>';
$params['page_data']['ticket_section'] = $ticketHtml;
$params['page_data']['ticket_css'] = $style;
// JS
$js = '<script>
(function() {
var apiBase = "' . $hostUrl . '/api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=ticket&pluginsaction=";
var token = "' . htmlspecialchars($token) . '";
window.VrTicketWallet = {
viewTicket: function(ticketId) {
var modal = document.getElementById("vrTicketModal") || createModal();
modal.classList.add("active");
var body = document.getElementById("vrTicketModalBody");
body.innerHTML = \'<div style="text-align:center;padding:40px;">加载中...</div>\';
$.ajax({
url: apiBase + "detail&id=" + ticketId,
headers: token ? {"X-Token": token} : {},
success: function(res) {
if (res.code === 0 && res.data.ticket) {
var t = res.data.ticket;
var statusMap = {0:{text:"未核销",class:"unverified"},1:{text:"已核销",class:"verified"},2:{text:"已退款",class:"refunded"}};
var status = statusMap[t.verify_status] || statusMap[0];
body.innerHTML = \'<div style="text-align:center;padding:20px;background:#fafafa;border-radius:12px;"><div id="vrQrcodeBox"></div></div>\' +
\'<div style="text-align:center;margin:16px 0;padding:12px;background:#f5f5f5;border-radius:8px;">\' +
\'<div style="font-size:12px;color:#999;margin-bottom:4px;">短码(人工核销)</div>\' +
\'<div style="font-size:20px;font-family:monospace;font-weight:700;letter-spacing:2px;">\' + t.short_code + \'</div></div>\' +
\'<div style="text-align:center;"><span class="vr-ticket-status \' + status.class + \'">\' + status.text + \'</span></div>\';
if (t.qr_payload) {
$("#vrQrcodeBox").qrcode({text: atob(t.qr_payload), width: 180, height: 180});
}
}
}
});
},
closeModal: function() {
var modal = document.getElementById("vrTicketModal");
if (modal) modal.classList.remove("active");
}
};
function createModal() {
var html = \'<div id="vrTicketModal" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:9999;display:none;align-items:center;justify-content:center;">\' +
\'<div style="background:#fff;border-radius:16px;width:90%;max-width:400px;padding:24px;"><div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">\' +
\'<div style="font-size:18px;font-weight:600;">电子票</div><button onclick="VrTicketWallet.closeModal()" style="width:28px;height:28px;border-radius:50%;background:#f0f0f0;border:none;cursor:pointer;">×</button></div>\' +
\'<div id="vrTicketModalBody"></div></div></div>\';
document.body.insertAdjacentHTML("beforeend", html);
var modal = document.getElementById("vrTicketModal");
modal.addEventListener("click", function(e) { if (e.target === modal) VrTicketWallet.closeModal(); });
return modal;
}
})();
</script>';
$params['page_data']['ticket_js'] = $js;
}
/**
* 在用户中心底部挂载票夹入口链接
*/
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 = '<div class="vr-wallet-entrance" style="margin-top:20px;">' .
'<a href="' . $hostUrl . '?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=index&pluginsaction=wallet" ' .
'style="display:inline-flex;align-items:center;padding:12px 20px;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);' .
'color:#fff;border-radius:8px;font-size:14px;font-weight:500;text-decoration:none;box-shadow:0 4px 12px rgba(102,126,234,0.3);">' .
'<span style="font-size:18px;margin-right:8px;">🎫</span> 我的电子票' .
'</a></div>';
return $walletLink;
}
}
?>