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
Council 2026-04-20 15:48:11 +08:00
parent df8353a697
commit 95346206dc
7 changed files with 315 additions and 281 deletions

@ -1 +1 @@
Subproject commit bde23d3195f5002e0b76031bd3d367e5ff00e1b2 Subproject commit 496271c468f7b0a93f495d8551451078d789d344

View File

@ -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";

View File

@ -243,23 +243,19 @@ class Admin extends Common
} }
if ($hardDelete) { if ($hardDelete) {
// 真删除:先检查是否有商品关联
$goods = \think\facade\Db::name('Goods') $goods = \think\facade\Db::name('Goods')
->where('vr_goods_config', 'like', '%"template_id":' . $id . '%') ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
->where('is_delete', 0) ->where('is_delete', 0)
->find(); ->find();
if (!empty($goods)) {
return DataReturn('该模板有关联商品,请先解除商品绑定后再删除', -402);
}
\think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete(); \think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete();
\app\plugins\vr_ticket\service\AuditService::log( \app\plugins\vr_ticket\service\AuditService::log(
\app\plugins\vr_ticket\service\AuditService::ACTION_DELETE_TEMPLATE, \app\plugins\vr_ticket\service\AuditService::ACTION_DELETE_TEMPLATE,
\app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE, \app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE,
$id, $id,
['name' => $template['name']], ['name' => $template['name'], 'has_goods' => !empty($goods)],
"模板: {$template['name']}" "模板: {$template['name']}"
); );
return DataReturn('删除成功', 0); return DataReturn('删除成功', 0, ['has_goods' => !empty($goods)]);
} }
// 软删除(禁用) // 软删除(禁用)
@ -874,23 +870,19 @@ class Admin extends Common
} }
if ($hardDelete) { if ($hardDelete) {
// 真删除:先检查是否有商品关联
$goods = \think\facade\Db::name('Goods') $goods = \think\facade\Db::name('Goods')
->where('vr_goods_config', 'like', '%"template_id":' . $id . '%') ->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
->where('is_delete', 0) ->where('is_delete', 0)
->find(); ->find();
if (!empty($goods)) {
return DataReturn('该模板有关联商品,请先解除商品绑定后再删除', -402);
}
\think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete(); \think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete();
\app\plugins\vr_ticket\service\AuditService::log( \app\plugins\vr_ticket\service\AuditService::log(
\app\plugins\vr_ticket\service\AuditService::ACTION_DELETE_TEMPLATE, \app\plugins\vr_ticket\service\AuditService::ACTION_DELETE_TEMPLATE,
\app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE, \app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE,
$id, $id,
['name' => $template['name']], ['name' => $template['name'], 'has_goods' => !empty($goods)],
"场馆: {$template['name']}" "场馆: {$template['name']}"
); );
return DataReturn('删除成功', 0); return DataReturn('删除成功', 0, ['has_goods' => !empty($goods)]);
} }

View File

@ -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)">&times;</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')}}

View File

@ -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')}}

View File

@ -146,8 +146,8 @@
<a class="am-modal-close" href="javascript:void(0)">&times;</a> <a class="am-modal-close" href="javascript:void(0)">&times;</a>
</div> </div>
<div class="am-modal-bd am-text-left"> <div class="am-modal-bd am-text-left">
<p style="color:#e00;font-weight:bold;margin-bottom:8px;">⚠️ 删除记录不会导致已上架商品内容变动</p> <p style="color:#e00;font-weight:bold;margin-bottom:8px;">⚠️ 删除后,关联商品的场馆信息将被自动清除</p>
<p style="color:#555;">若需要同步场馆信息到已发布商品,请编辑对应商品并保存</p> <p style="color:#555;">删除前已购买的用户不受影响(已有购买快照)</p>
</div> </div>
<div class="am-modal-footer"> <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-default am-radius" data-am-modal-cancel>取消</button>

154
shopxo/test_ticket.php Normal file
View File

@ -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";