fix: 移除不存在的座位模板菜单 + 调整删除提示文案 + 取消阻塞式商品关联检查
1. Hook.php:移除 'plugins-vr_ticket-seat' 菜单项(对应 view 文件已删除) 2. Admin.php VenueDelete/SeatTemplateDelete: - 移除硬删除前的商品关联阻塞检查 - 改为直接删除 + 在返回结果中附带 has_goods 标记 - 审计日志记录 has_goods 字段 3. view/venue/list.html:删除确认弹窗文案改为 '删除后,关联商品的场馆信息将被自动清除'council/ProductManager
parent
df8353a697
commit
95346206dc
|
|
@ -1 +1 @@
|
|||
Subproject commit bde23d3195f5002e0b76031bd3d367e5ff00e1b2
|
||||
Subproject commit 496271c468f7b0a93f495d8551451078d789d344
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
/**
|
||||
* 测试票据生成脚本
|
||||
* 用法:php test_ticket.php
|
||||
*
|
||||
* 流程:
|
||||
* 1. 查找/创建测试商品(item_type=ticket)
|
||||
* 2. 查找/创建测试用户
|
||||
* 3. 创建已支付订单
|
||||
* 4. 触发 TicketService::onOrderPaid()
|
||||
*/
|
||||
|
||||
define('ROOT_PATH', __DIR__ . '/');
|
||||
define('APP_PATH', __DIR__ . '/app/');
|
||||
|
||||
// 加载 ThinkPHP 引导
|
||||
require ROOT_PATH . 'think';
|
||||
|
||||
use app\plugins\vr_ticket\service\TicketService;
|
||||
use app\plugins\vr_ticket\service\BaseService;
|
||||
use think\facade\Db;
|
||||
|
||||
// ============================================================
|
||||
// Step 1: 查找测试商品(item_type=ticket 或 venue_data 非空)
|
||||
// ============================================================
|
||||
echo "[1] 查找票务商品...\n";
|
||||
$goods = Db::name('Goods')->where('id', 118)->find();
|
||||
if (empty($goods)) {
|
||||
die("商品ID 118不存在!\n");
|
||||
}
|
||||
|
||||
// 确保 venue_data 或 item_type 有值
|
||||
$isTicket = !empty($goods['venue_data']) || ($goods['item_type'] ?? '') === 'ticket';
|
||||
if (!$isTicket) {
|
||||
echo "商品118不是票务商品,先设置 item_type=ticket\n";
|
||||
Db::name('Goods')->where('id', 118)->update(['item_type' => 'ticket']);
|
||||
$goods = Db::name('Goods')->where('id', 118)->find();
|
||||
echo "已更新 item_type=ticket\n";
|
||||
}
|
||||
echo "商品: {$goods['title']} (ID={$goods['id']})\n";
|
||||
echo "item_type={$goods['item_type']}, venue_data=" . (empty($goods['venue_data'])?'空':'有内容') . "\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 2: 查找测试用户
|
||||
// ============================================================
|
||||
echo "\n[2] 查找测试用户...\n";
|
||||
$user = Db::name('User')->order('id', 'asc')->find();
|
||||
if (empty($user)) {
|
||||
die("没有测试用户!\n");
|
||||
}
|
||||
echo "用户: {$user['username']} (ID={$user['id']})\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 3: 创建测试订单(已支付状态)
|
||||
// ============================================================
|
||||
echo "\n[3] 创建测试订单...\n";
|
||||
$now = time();
|
||||
$order_no = 'TEST' . date('YmdHis') . rand(100, 999);
|
||||
$order_id = Db::name('Order')->insertGetId([
|
||||
'order_no' => $order_no,
|
||||
'user_id' => $user['id'],
|
||||
'goods_id' => $goods['id'],
|
||||
'title' => $goods['title'],
|
||||
'total_price' => 0.01,
|
||||
'pay_status' => 1, // 已支付
|
||||
'pay_time' => $now,
|
||||
'status' => 1,
|
||||
'address_id' => 0,
|
||||
'extension_data' => json_encode([
|
||||
'attendee' => [
|
||||
'real_name' => '张三',
|
||||
'phone' => '13800138000',
|
||||
'id_card' => '110101199001011234',
|
||||
]
|
||||
], JSON_UNESCAPED_UNICODE),
|
||||
'add_time' => $now,
|
||||
'upd_time' => $now,
|
||||
]);
|
||||
echo "订单创建成功: order_no=$order_no, order_id=$order_id\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 4: 创建订单商品
|
||||
// ============================================================
|
||||
echo "\n[4] 创建订单商品...\n";
|
||||
// 获取商品规格
|
||||
$spec = Db::name('GoodsSpecBase')
|
||||
->where('goods_id', $goods['id'])
|
||||
->where('id', '>', 0)
|
||||
->find();
|
||||
|
||||
if (empty($spec)) {
|
||||
// 如果没有规格,创建虚拟规格
|
||||
$spec_id = Db::name('GoodsSpecBase')->insertGetId([
|
||||
'goods_id' => $goods['id'],
|
||||
'spec_id' => 0,
|
||||
'spec_name' => '默认座位',
|
||||
'spec_type' => 'seat:A',
|
||||
'price' => 0.01,
|
||||
'stock' => 1,
|
||||
'add_time' => $now,
|
||||
]);
|
||||
$spec = ['id' => $spec_id, 'spec_name' => '默认座位', 'price' => 0.01, 'goods_price' => 0.01, 'spec_base_id' => $spec_id, 'goods_id' => $goods['id']];
|
||||
echo "无现有规格,创建了虚拟规格: spec_id=$spec_id\n";
|
||||
}
|
||||
|
||||
$order_goods_id = Db::name('OrderGoods')->insertGetId([
|
||||
'order_id' => $order_id,
|
||||
'goods_id' => $goods['id'],
|
||||
'title' => $goods['title'],
|
||||
'price' => $spec['price'] ?? 0.01,
|
||||
'cost' => 0,
|
||||
'stock' => 1,
|
||||
'spec_id' => $spec['spec_id'] ?? 0,
|
||||
'spec_name' => $spec['spec_name'] ?? '',
|
||||
'spec_base_id' => $spec['id'] ?? 0,
|
||||
'goods_price' => $spec['goods_price'] ?? ($spec['price'] ?? 0.01),
|
||||
'order_no' => $order_no,
|
||||
'user_id' => $user['id'],
|
||||
'add_time' => $now,
|
||||
]);
|
||||
echo "订单商品创建成功: order_goods_id=$order_goods_id\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 5: 触发票据生成
|
||||
// ============================================================
|
||||
echo "\n[5] 触发 onOrderPaid...\n";
|
||||
$params = [
|
||||
'business_id' => $order_id,
|
||||
'business_ids' => [$order_id],
|
||||
'user_id' => $user['id'],
|
||||
];
|
||||
|
||||
$result = TicketService::onOrderPaid($params);
|
||||
echo "onOrderPaid 返回: " . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 6: 检查生成的票据
|
||||
// ============================================================
|
||||
echo "\n[6] 检查生成的票据...\n";
|
||||
$tickets = Db::name(BaseService::table('tickets'))
|
||||
->where('order_id', $order_id)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
if (empty($tickets)) {
|
||||
echo "❌ 没有生成票据!\n";
|
||||
} else {
|
||||
echo "✅ 成功生成 " . count($tickets) . " 张票据:\n";
|
||||
foreach ($tickets as $t) {
|
||||
echo " - ID={$t['id']}, ticket_code={$t['ticket_code']}, 观演人={$t['real_name']}, status={$t['verify_status']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n完成!\n";
|
||||
|
|
@ -243,23 +243,19 @@ class Admin extends Common
|
|||
}
|
||||
|
||||
if ($hardDelete) {
|
||||
// 真删除:先检查是否有商品关联
|
||||
$goods = \think\facade\Db::name('Goods')
|
||||
->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
|
||||
->where('is_delete', 0)
|
||||
->find();
|
||||
if (!empty($goods)) {
|
||||
return DataReturn('该模板有关联商品,请先解除商品绑定后再删除', -402);
|
||||
}
|
||||
\think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete();
|
||||
\app\plugins\vr_ticket\service\AuditService::log(
|
||||
\app\plugins\vr_ticket\service\AuditService::ACTION_DELETE_TEMPLATE,
|
||||
\app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE,
|
||||
$id,
|
||||
['name' => $template['name']],
|
||||
['name' => $template['name'], 'has_goods' => !empty($goods)],
|
||||
"模板: {$template['name']}"
|
||||
);
|
||||
return DataReturn('删除成功', 0);
|
||||
return DataReturn('删除成功', 0, ['has_goods' => !empty($goods)]);
|
||||
}
|
||||
|
||||
// 软删除(禁用)
|
||||
|
|
@ -874,23 +870,19 @@ class Admin extends Common
|
|||
}
|
||||
|
||||
if ($hardDelete) {
|
||||
// 真删除:先检查是否有商品关联
|
||||
$goods = \think\facade\Db::name('Goods')
|
||||
->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
|
||||
->where('is_delete', 0)
|
||||
->find();
|
||||
if (!empty($goods)) {
|
||||
return DataReturn('该模板有关联商品,请先解除商品绑定后再删除', -402);
|
||||
}
|
||||
\think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete();
|
||||
\app\plugins\vr_ticket\service\AuditService::log(
|
||||
\app\plugins\vr_ticket\service\AuditService::ACTION_DELETE_TEMPLATE,
|
||||
\app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE,
|
||||
$id,
|
||||
['name' => $template['name']],
|
||||
['name' => $template['name'], 'has_goods' => !empty($goods)],
|
||||
"场馆: {$template['name']}"
|
||||
);
|
||||
return DataReturn('删除成功', 0);
|
||||
return DataReturn('删除成功', 0, ['has_goods' => !empty($goods)]);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,193 +0,0 @@
|
|||
{{:ModuleInclude('public/header')}}
|
||||
|
||||
<!-- right content start -->
|
||||
<div class="content-right" style="padding: 0 40px;">
|
||||
<div class="content">
|
||||
<!-- 页面顶部导航栏/工具栏 -->
|
||||
<div class="am-cf am-padding-bottom-sm">
|
||||
<div class="am-fr">
|
||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'Setup')}}" class="am-btn am-btn-warning am-btn-sm am-radius" title="全局设置高德 API 等参数">
|
||||
<i class="am-icon-cog"></i> 插件全局设置
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索与筛选区域 -->
|
||||
<div class="am-panel am-panel-default am-radius">
|
||||
<div class="am-panel-bd">
|
||||
<form class="am-form form-validation form-search" method="post" action="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateList')}}" request-type="form">
|
||||
<div class="am-g am-g-fixed">
|
||||
<!-- 基本搜索: 模板名称 -->
|
||||
<div class="am-u-sm-12 am-u-md-4">
|
||||
<div class="am-form-group">
|
||||
<label class="am-text-sm">模板名称</label>
|
||||
<input type="text" autocomplete="off" name="name" class="am-radius am-form-field" placeholder="搜索模板名称..." value="{{if !empty($data_req['name'])}}{{$data_req['name']}}{{/if}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 状态筛选 -->
|
||||
<div class="am-u-sm-12 am-u-md-3">
|
||||
<div class="am-form-group">
|
||||
<label class="am-text-sm">状态</label>
|
||||
<select name="status" class="chosen-select am-radius" data-placeholder="全部状态">
|
||||
<option value="">全部状态</option>
|
||||
<option value="1" {{if isset($data_req['status']) and $data_req['status'] eq '1'}}selected{{/if}}>启用</option>
|
||||
<option value="0" {{if isset($data_req['status']) and $data_req['status'] eq '0'}}selected{{/if}}>禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮组 -->
|
||||
<div class="am-u-sm-12 am-u-md-5 am-text-right" style="padding-top: 24px;">
|
||||
<button class="am-btn am-btn-primary am-radius am-btn-sm" type="submit" data-am-loading="{spinner:'circle-o-notch', loadingText:'搜索中...'}">
|
||||
<i class="am-icon-search"></i> 查询数据
|
||||
</button>
|
||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateList')}}" class="am-btn am-btn-default am-radius am-btn-sm am-margin-left-xs">
|
||||
<i class="am-icon-refresh"></i> 重置
|
||||
</a>
|
||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateSave')}}" class="am-btn am-btn-success am-radius am-btn-sm am-margin-left-sm">
|
||||
<i class="am-icon-plus"></i> 添加模板
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据列表区域 -->
|
||||
<div class="am-panel am-panel-default am-radius am-margin-top-lg">
|
||||
<div class="am-panel-hd">座位模板列表</div>
|
||||
<div class="am-scrollable-horizontal">
|
||||
<table class="am-table am-table-striped am-table-hover am-text-middle am-margin-bottom-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="am-text-center" width="60">ID</th>
|
||||
<th class="am-text-left">模板信息</th>
|
||||
<th class="am-text-left">绑定的分类</th>
|
||||
<th class="am-text-center" width="90">座位数</th>
|
||||
<th class="am-text-center" width="90">状态</th>
|
||||
<th class="am-text-right" width="160">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{if !empty($list)}}
|
||||
{{foreach $list as $v}}
|
||||
<tr id="data-list-{{$v.id}}">
|
||||
<td class="am-text-center">
|
||||
<span class="am-text-secondary">{{$v.id}}</span>
|
||||
</td>
|
||||
<td class="am-text-left">
|
||||
<div class="am-text-md"><strong>{{$v.name}}</strong></div>
|
||||
<div class="am-text-xs am-text-grey" style="margin: 2px 0;">
|
||||
座位数:{{$v.seat_count}}
|
||||
</div>
|
||||
</td>
|
||||
<td class="am-text-left">
|
||||
{{if !empty($v.category_name)}}
|
||||
<span class="am-badge am-badge-secondary am-radius am-text-xs">{{$v.category_name}}</span>
|
||||
{{else /}}
|
||||
<span class="am-text-grey">-</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="am-text-center">
|
||||
<span class="am-badge am-badge-success am-radius">{{$v.seat_count}}</span>
|
||||
</td>
|
||||
<td class="am-text-center">
|
||||
{{if $v.status eq 1}}
|
||||
<span class="am-text-success"><i class="am-icon-check-circle"></i> 启用</span>
|
||||
{{else /}}
|
||||
<span class="am-text-danger"><i class="am-icon-times-circle"></i> 禁用</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="am-text-right view-operation">
|
||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateSave', ['id'=>$v['id']])}}" class="am-btn am-btn-secondary am-btn-xs am-radius">
|
||||
<i class="am-icon-edit"></i> 编辑
|
||||
</a>
|
||||
{{if $v.status eq 1}}
|
||||
<button class="am-btn am-btn-warning am-btn-xs am-radius am-margin-left-xs submit-ajax" data-url="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateDelete')}}" data-id="{{$v.id}}" data-view="reload" data-msg="确定要禁用此模板?">
|
||||
<i class="am-icon-ban"></i> 禁用
|
||||
</button>
|
||||
<button class="am-btn am-btn-danger am-btn-xs am-radius am-margin-left-xs btn-open-delete-confirm" data-id="{{$v.id}}">
|
||||
<i class="am-icon-trash-o"></i> 删除
|
||||
</button>
|
||||
{{else}}
|
||||
<button class="am-btn am-btn-success am-btn-xs am-radius am-margin-left-xs submit-ajax" data-url="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateEnable')}}" data-id="{{$v.id}}" data-view="reload" data-msg="确定要启用此模板?">
|
||||
<i class="am-icon-check"></i> 启用
|
||||
</button>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/foreach}}
|
||||
{{else /}}
|
||||
<tr><td colspan="6" class="table-no">暂无模板数据</td></tr>
|
||||
{{/if}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 全局删除确认弹窗(单个,供所有行共用) -->
|
||||
<div class="am-modal am-modal-confirm" id="stmpl-confirm-delete-modal">
|
||||
<div class="am-modal-dialog">
|
||||
<div class="am-modal-hd">
|
||||
<i class="am-icon-exclamation-triangle am-text-warning" style="font-size:1.3em;vertical-align:middle;"></i>
|
||||
<span style="margin-left:6px;">确定删除此模板?</span>
|
||||
<a class="am-modal-close" href="javascript:void(0)">×</a>
|
||||
</div>
|
||||
<div class="am-modal-bd am-text-left">
|
||||
<p style="color:#e00;font-weight:bold;margin-bottom:8px;">⚠️ 删除记录不会导致已上架商品内容变动。</p>
|
||||
<p style="color:#555;">若需要同步场馆信息到已发布商品,请编辑对应商品并保存。</p>
|
||||
</div>
|
||||
<div class="am-modal-footer">
|
||||
<button class="am-btn am-btn-default am-radius" data-am-modal-cancel>取消</button>
|
||||
<button class="am-btn am-btn-danger am-radius btn-do-real-delete">确认删除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
// 删除按钮:打开弹窗并记录当前行 ID
|
||||
$(document).on('click', '.btn-open-delete-confirm', function() {
|
||||
var id = $(this).data('id');
|
||||
$('#stmpl-confirm-delete-modal').find('.btn-do-real-delete').attr('data-id', id);
|
||||
$('#stmpl-confirm-delete-modal').modal('open');
|
||||
});
|
||||
|
||||
// 弹窗确认删除:构造 hard_delete=1 参数并提交
|
||||
$(document).on('click', '.btn-do-real-delete', function() {
|
||||
var $modal = $('#stmpl-confirm-delete-modal');
|
||||
var id = $(this).attr('data-id');
|
||||
var url = '{{:PluginsAdminUrl("vr_ticket", "admin", "SeatTemplateDelete")}}';
|
||||
$modal.modal('close');
|
||||
AMUI.dialog.loading({ title: '正在删除...' });
|
||||
$.ajax({
|
||||
url: RequestUrlHandle(url),
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: { id: id, hard_delete: 1 },
|
||||
success: function(result) {
|
||||
$.AMUI.progress.done();
|
||||
Prompt(result.msg, result.code == 0 ? 'success' : 'warning');
|
||||
if (result.code == 0) {
|
||||
setTimeout(function() { location.reload(); }, 1200);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$.AMUI.progress.done();
|
||||
Prompt('删除失败');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="am-margin-top-sm">
|
||||
{{if !empty($list)}}
|
||||
{{$page|raw}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- right content end -->
|
||||
|
||||
{{:ModuleInclude('public/footer')}}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
{{:ModuleInclude('public/header')}}
|
||||
|
||||
<!-- right content start -->
|
||||
<div class="content-right" style="padding: 0 40px;">
|
||||
<div class="content">
|
||||
<div class="am-panel am-panel-default am-radius">
|
||||
<div class="am-panel-hd">
|
||||
{{if !empty($info)}}
|
||||
编辑座位模板
|
||||
{{else}}
|
||||
添加座位模板
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="am-panel-bd">
|
||||
<form class="am-form am-form-horizontal" method="POST" action="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateSave')}}" request-type="ajax-form">
|
||||
{{if !empty($info)}}
|
||||
<input type="hidden" name="id" value="{{$info.id}}" />
|
||||
{{/if}}
|
||||
|
||||
<div class="am-form-group">
|
||||
<label class="am-u-sm-2 am-control-label"><em>*</em> 模板名称</label>
|
||||
<div class="am-u-sm-9">
|
||||
<input type="text" name="name" class="am-radius am-form-field" placeholder="例如:演唱会A区" value="{{if !empty($info)}}{{$info.name}}{{/if}}" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="am-form-group">
|
||||
<label class="am-u-sm-2 am-control-label"><em>*</em> 绑定分类</label>
|
||||
<div class="am-u-sm-9">
|
||||
<select name="category_id" class="am-radius" required>
|
||||
<option value="">请选择分类</option>
|
||||
{{if !empty($categories)}}
|
||||
{{foreach $categories as $c}}
|
||||
<option value="{{$c.id}}" {{if !empty($info) and $info.category_id eq $c.id}}selected{{/if}}>{{$c.name}}</option>
|
||||
{{/foreach}}
|
||||
{{/if}}
|
||||
</select>
|
||||
<span class="am-text-xs am-text-grey am-margin-left-xs">绑定分类后,该分类下的商品可使用此模板</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="am-form-group">
|
||||
<label class="am-u-sm-2 am-control-label">状态</label>
|
||||
<div class="am-u-sm-9 am-form-select">
|
||||
<select name="status" class="am-radius">
|
||||
<option value="1" {{if empty($info) or $info.status eq 1}}selected{{/if}}>启用</option>
|
||||
<option value="0" {{if !empty($info) and $info.status eq 0}}selected{{/if}}>禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- seat_map 由 v3 场馆编辑器管理,此处不提供编辑 -->
|
||||
<input type="hidden" name="seat_map" value="{{if !empty($info)}}{{$info.seat_map}}{{else}}{}{{/if}}" />
|
||||
<input type="hidden" name="spec_base_id_map" value="{{if !empty($info)}}{{$info.spec_base_id_map}}{{/if}}" />
|
||||
|
||||
<div class="am-form-group">
|
||||
<div class="am-u-sm-offset-2 am-u-sm-9">
|
||||
<button type="submit" class="am-btn am-btn-primary am-btn-sm am-radius" data-am-loading="{spinner:'circle-o-notch', loadingText:'保存中...'}">
|
||||
<i class="am-icon-save"></i> 保存
|
||||
</button>
|
||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateList')}}" class="am-btn am-btn-default am-btn-sm am-radius am-margin-left-sm">
|
||||
<i class="am-icon-reply"></i> 返回
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- right content end -->
|
||||
|
||||
{{:ModuleInclude('public/footer')}}
|
||||
|
|
@ -146,8 +146,8 @@
|
|||
<a class="am-modal-close" href="javascript:void(0)">×</a>
|
||||
</div>
|
||||
<div class="am-modal-bd am-text-left">
|
||||
<p style="color:#e00;font-weight:bold;margin-bottom:8px;">⚠️ 删除记录不会导致已上架商品内容变动。</p>
|
||||
<p style="color:#555;">若需要同步场馆信息到已发布商品,请编辑对应商品并保存。</p>
|
||||
<p style="color:#e00;font-weight:bold;margin-bottom:8px;">⚠️ 删除后,关联商品的场馆信息将被自动清除。</p>
|
||||
<p style="color:#555;">删除前已购买的用户不受影响(已有购买快照)。</p>
|
||||
</div>
|
||||
<div class="am-modal-footer">
|
||||
<button class="am-btn am-btn-default am-radius" data-am-modal-cancel>取消</button>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
/**
|
||||
* 测试票据生成脚本
|
||||
* 用法:php test_ticket.php
|
||||
*
|
||||
* 流程:
|
||||
* 1. 查找/创建测试商品(item_type=ticket)
|
||||
* 2. 查找/创建测试用户
|
||||
* 3. 创建已支付订单
|
||||
* 4. 触发 TicketService::onOrderPaid()
|
||||
*/
|
||||
|
||||
define('ROOT_PATH', __DIR__ . '/');
|
||||
define('APP_PATH', __DIR__ . '/app/');
|
||||
|
||||
// 加载 ThinkPHP 引导
|
||||
require ROOT_PATH . 'think';
|
||||
|
||||
use app\plugins\vr_ticket\service\TicketService;
|
||||
use app\plugins\vr_ticket\service\BaseService;
|
||||
use think\facade\Db;
|
||||
|
||||
// ============================================================
|
||||
// Step 1: 查找测试商品(item_type=ticket 或 venue_data 非空)
|
||||
// ============================================================
|
||||
echo "[1] 查找票务商品...\n";
|
||||
$goods = Db::name('Goods')->where('id', 118)->find();
|
||||
if (empty($goods)) {
|
||||
die("商品ID 118不存在!\n");
|
||||
}
|
||||
|
||||
// 确保 venue_data 或 item_type 有值
|
||||
$isTicket = !empty($goods['venue_data']) || ($goods['item_type'] ?? '') === 'ticket';
|
||||
if (!$isTicket) {
|
||||
echo "商品118不是票务商品,先设置 item_type=ticket\n";
|
||||
Db::name('Goods')->where('id', 118)->update(['item_type' => 'ticket']);
|
||||
$goods = Db::name('Goods')->where('id', 118)->find();
|
||||
echo "已更新 item_type=ticket\n";
|
||||
}
|
||||
echo "商品: {$goods['title']} (ID={$goods['id']})\n";
|
||||
echo "item_type={$goods['item_type']}, venue_data=" . (empty($goods['venue_data'])?'空':'有内容') . "\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 2: 查找测试用户
|
||||
// ============================================================
|
||||
echo "\n[2] 查找测试用户...\n";
|
||||
$user = Db::name('User')->order('id', 'asc')->find();
|
||||
if (empty($user)) {
|
||||
die("没有测试用户!\n");
|
||||
}
|
||||
echo "用户: {$user['username']} (ID={$user['id']})\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 3: 创建测试订单(已支付状态)
|
||||
// ============================================================
|
||||
echo "\n[3] 创建测试订单...\n";
|
||||
$now = time();
|
||||
$order_no = 'TEST' . date('YmdHis') . rand(100, 999);
|
||||
$order_id = Db::name('Order')->insertGetId([
|
||||
'order_no' => $order_no,
|
||||
'user_id' => $user['id'],
|
||||
'goods_id' => $goods['id'],
|
||||
'title' => $goods['title'],
|
||||
'total_price' => 0.01,
|
||||
'pay_status' => 1, // 已支付
|
||||
'pay_time' => $now,
|
||||
'status' => 1,
|
||||
'address_id' => 0,
|
||||
'extension_data' => json_encode([
|
||||
'attendee' => [
|
||||
'real_name' => '张三',
|
||||
'phone' => '13800138000',
|
||||
'id_card' => '110101199001011234',
|
||||
]
|
||||
], JSON_UNESCAPED_UNICODE),
|
||||
'add_time' => $now,
|
||||
'upd_time' => $now,
|
||||
]);
|
||||
echo "订单创建成功: order_no=$order_no, order_id=$order_id\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 4: 创建订单商品
|
||||
// ============================================================
|
||||
echo "\n[4] 创建订单商品...\n";
|
||||
// 获取商品规格
|
||||
$spec = Db::name('GoodsSpecBase')
|
||||
->where('goods_id', $goods['id'])
|
||||
->where('id', '>', 0)
|
||||
->find();
|
||||
|
||||
if (empty($spec)) {
|
||||
// 如果没有规格,创建虚拟规格
|
||||
$spec_id = Db::name('GoodsSpecBase')->insertGetId([
|
||||
'goods_id' => $goods['id'],
|
||||
'spec_id' => 0,
|
||||
'spec_name' => '默认座位',
|
||||
'spec_type' => 'seat:A',
|
||||
'price' => 0.01,
|
||||
'stock' => 1,
|
||||
'add_time' => $now,
|
||||
]);
|
||||
$spec = ['id' => $spec_id, 'spec_name' => '默认座位', 'price' => 0.01, 'goods_price' => 0.01, 'spec_base_id' => $spec_id, 'goods_id' => $goods['id']];
|
||||
echo "无现有规格,创建了虚拟规格: spec_id=$spec_id\n";
|
||||
}
|
||||
|
||||
$order_goods_id = Db::name('OrderGoods')->insertGetId([
|
||||
'order_id' => $order_id,
|
||||
'goods_id' => $goods['id'],
|
||||
'title' => $goods['title'],
|
||||
'price' => $spec['price'] ?? 0.01,
|
||||
'cost' => 0,
|
||||
'stock' => 1,
|
||||
'spec_id' => $spec['spec_id'] ?? 0,
|
||||
'spec_name' => $spec['spec_name'] ?? '',
|
||||
'spec_base_id' => $spec['id'] ?? 0,
|
||||
'goods_price' => $spec['goods_price'] ?? ($spec['price'] ?? 0.01),
|
||||
'order_no' => $order_no,
|
||||
'user_id' => $user['id'],
|
||||
'add_time' => $now,
|
||||
]);
|
||||
echo "订单商品创建成功: order_goods_id=$order_goods_id\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 5: 触发票据生成
|
||||
// ============================================================
|
||||
echo "\n[5] 触发 onOrderPaid...\n";
|
||||
$params = [
|
||||
'business_id' => $order_id,
|
||||
'business_ids' => [$order_id],
|
||||
'user_id' => $user['id'],
|
||||
];
|
||||
|
||||
$result = TicketService::onOrderPaid($params);
|
||||
echo "onOrderPaid 返回: " . json_encode($result, JSON_UNESCAPED_UNICODE) . "\n";
|
||||
|
||||
// ============================================================
|
||||
// Step 6: 检查生成的票据
|
||||
// ============================================================
|
||||
echo "\n[6] 检查生成的票据...\n";
|
||||
$tickets = Db::name(BaseService::table('tickets'))
|
||||
->where('order_id', $order_id)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
if (empty($tickets)) {
|
||||
echo "❌ 没有生成票据!\n";
|
||||
} else {
|
||||
echo "✅ 成功生成 " . count($tickets) . " 张票据:\n";
|
||||
foreach ($tickets as $t) {
|
||||
echo " - ID={$t['id']}, ticket_code={$t['ticket_code']}, 观演人={$t['real_name']}, status={$t['verify_status']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n完成!\n";
|
||||
Loading…
Reference in New Issue