2026-04-15 11:58:48 +00:00
|
|
|
|
<?php
|
|
|
|
|
|
/**
|
|
|
|
|
|
* VR票务插件 - 座位 SKU 服务
|
|
|
|
|
|
*
|
|
|
|
|
|
* 核心业务:批量生成座位级 SKU(spec_base + spec_value)
|
|
|
|
|
|
* 旁路 GoodsSpecificationsInsert(),直接 SQL INSERT
|
|
|
|
|
|
*
|
|
|
|
|
|
* @package vr_ticket\service
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
namespace app\pluginsr_ticket\service;
|
2026-04-15 11:58:48 +00:00
|
|
|
|
|
|
|
|
|
|
class SeatSkuService extends BaseService
|
|
|
|
|
|
{
|
|
|
|
|
|
/** @var int 分批处理每批条数 */
|
|
|
|
|
|
const BATCH_SIZE = 500;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 批量生成座位级 SKU
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static function BatchGenerate(int $goodsId, int $seatTemplateId): array
|
|
|
|
|
|
{
|
|
|
|
|
|
$goodsId = intval($goodsId);
|
|
|
|
|
|
$seatTemplateId = intval($seatTemplateId);
|
|
|
|
|
|
|
|
|
|
|
|
if ($goodsId <= 0 || $seatTemplateId <= 0) {
|
|
|
|
|
|
return ['code' => -1, 'msg' => '参数错误:goodsId 或 seatTemplateId 无效'];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 加载座位模板
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$template = hinkacade\Db::name(self::table('seat_templates'))
|
2026-04-15 11:58:48 +00:00
|
|
|
|
->where('id', $seatTemplateId)
|
|
|
|
|
|
->find();
|
|
|
|
|
|
if (empty($template)) {
|
|
|
|
|
|
return ['code' => -2, 'msg' => "座位模板 {$seatTemplateId} 不存在"];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 解析 seat_map
|
|
|
|
|
|
$seatMap = json_decode($template['seat_map'] ?? '{}', true);
|
2026-04-16 16:46:00 +00:00
|
|
|
|
|
|
|
|
|
|
$rooms = $seatMap['rooms'] ?? [];
|
|
|
|
|
|
if (empty($rooms)) {
|
|
|
|
|
|
// 向下兼容旧结构
|
|
|
|
|
|
if (!empty($seatMap['map'])) {
|
|
|
|
|
|
$rooms = [
|
|
|
|
|
|
[
|
|
|
|
|
|
'id' => 'room_default',
|
|
|
|
|
|
'name' => '默认放映室',
|
|
|
|
|
|
'map' => $seatMap['map'],
|
|
|
|
|
|
'seats' => $seatMap['seats'] ?? [],
|
|
|
|
|
|
'sections' => $seatMap['sections'] ?? []
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return ['code' => -3, 'msg' => '座位模板 seat_map 数据无效'];
|
|
|
|
|
|
}
|
2026-04-15 11:58:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 14:02:03 +00:00
|
|
|
|
$venueName = $seatMap['venue']['name'] ?? $template['name'] ?? '未命名场馆';
|
|
|
|
|
|
$specTypeIds = self::ensureVrSpecTypes($goodsId, $venueName);
|
2026-04-15 11:58:48 +00:00
|
|
|
|
if ($specTypeIds['code'] !== 0) {
|
|
|
|
|
|
return $specTypeIds;
|
|
|
|
|
|
}
|
|
|
|
|
|
$typeVenue = $specTypeIds['data']['$vr-场馆'];
|
|
|
|
|
|
$typeZone = $specTypeIds['data']['$vr-分区'];
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$typeTime = $specTypeIds['data']['$vr-时段']; // 时段留作可选,前端默认传“不限时段”
|
2026-04-15 11:58:48 +00:00
|
|
|
|
$typeSeat = $specTypeIds['data']['$vr-座位号'];
|
|
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$seatsToInsert = [];
|
|
|
|
|
|
// 遍历 rooms
|
|
|
|
|
|
foreach ($rooms as $room) {
|
|
|
|
|
|
$roomId = $room['id'] ?? 'room_default';
|
|
|
|
|
|
$roomName = $room['name'] ?? '默认放映室';
|
|
|
|
|
|
|
|
|
|
|
|
$sectionPrices = [];
|
|
|
|
|
|
foreach (($room['sections'] ?? []) as $section) {
|
|
|
|
|
|
$sectionPrices[$section['name'] ?? ''] = floatval($section['price'] ?? 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$map = $room['map'] ?? [];
|
|
|
|
|
|
$seatsData = $room['seats'] ?? [];
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($map as $rowIndex => $rowStr) {
|
|
|
|
|
|
$rowLabel = chr(65 + $rowIndex);
|
|
|
|
|
|
$chars = mb_str_split($rowStr);
|
|
|
|
|
|
foreach ($chars as $colIndex => $char) {
|
|
|
|
|
|
if ($char === '_' || $char === '-' || !isset($seatsData[$char])) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
$seatInfo = $seatsData[$char];
|
|
|
|
|
|
$zoneName = $seatInfo['label'] ?? ($seatInfo['zone'] ?? ($seatInfo['section'] ?? '默认区'));
|
|
|
|
|
|
|
|
|
|
|
|
$seatPrice = floatval($seatInfo['price'] ?? 0);
|
|
|
|
|
|
if ($seatPrice == 0 && isset($sectionPrices[$zoneName])) {
|
|
|
|
|
|
$seatPrice = $sectionPrices[$zoneName];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$seatId = $roomName . '_' . $rowLabel . '_' . ($colIndex + 1);
|
|
|
|
|
|
$seatDisplayName = $roomName . ' ' . $zoneName . ' ' . $rowLabel . ($colIndex + 1);
|
|
|
|
|
|
|
|
|
|
|
|
$seatsToInsert[$seatId] = [
|
|
|
|
|
|
'room' => $roomName,
|
|
|
|
|
|
'zone' => $zoneName,
|
|
|
|
|
|
'row' => $rowIndex,
|
|
|
|
|
|
'col' => $colIndex,
|
|
|
|
|
|
'char' => $char,
|
|
|
|
|
|
'label' => $seatDisplayName,
|
|
|
|
|
|
'price' => $seatPrice
|
|
|
|
|
|
];
|
2026-04-15 11:58:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-16 16:46:00 +00:00
|
|
|
|
|
|
|
|
|
|
if (empty($seatsToInsert)) {
|
|
|
|
|
|
return ['code' => -3, 'msg' => '无有效座位可生成'];
|
2026-04-15 11:58:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
// 查询已有 SKU
|
|
|
|
|
|
$existingBases = hinkacade\Db::name('GoodsSpecBase')
|
|
|
|
|
|
->where('goods_id', $goodsId)
|
|
|
|
|
|
->column('id', 'id');
|
|
|
|
|
|
$existingValues = [];
|
|
|
|
|
|
if (!empty($existingBases)) {
|
|
|
|
|
|
$valueRows = hinkacade\Db::name('GoodsSpecValue')
|
|
|
|
|
|
->where('goods_id', $goodsId)
|
|
|
|
|
|
->where('goods_spec_base_id', 'in', array_keys($existingBases))
|
|
|
|
|
|
->where('type', $typeSeat)
|
|
|
|
|
|
->column('value', 'goods_spec_base_id');
|
|
|
|
|
|
foreach ($valueRows as $baseId => $val) {
|
|
|
|
|
|
$existingValues[$val] = $baseId;
|
2026-04-15 11:58:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$now = time();
|
|
|
|
|
|
$generatedCount = 0;
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$specBaseIdMap = [];
|
|
|
|
|
|
|
|
|
|
|
|
hinkacade\Db::startTrans();
|
|
|
|
|
|
try {
|
|
|
|
|
|
$baseBatch = [];
|
|
|
|
|
|
$valueBatch = [];
|
|
|
|
|
|
foreach ($seatsToInsert as $seatId => $s) {
|
|
|
|
|
|
if (isset($existingValues[$s['label']])) {
|
|
|
|
|
|
$baseId = $existingValues[$s['label']];
|
|
|
|
|
|
$specBaseIdMap[$seatId] = [
|
|
|
|
|
|
'spec_base_id' => $baseId,
|
|
|
|
|
|
'room' => $s['room'],
|
|
|
|
|
|
'zone' => $s['zone'],
|
|
|
|
|
|
'row' => $s['row'],
|
|
|
|
|
|
'col' => $s['col']
|
|
|
|
|
|
];
|
|
|
|
|
|
continue; // 已经存在,查出来返回映射即可
|
|
|
|
|
|
}
|
2026-04-15 11:58:48 +00:00
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$baseId = hinkacade\Db::name('GoodsSpecBase')->insertGetId([
|
|
|
|
|
|
'goods_id' => $goodsId,
|
|
|
|
|
|
'price' => $s['price'],
|
|
|
|
|
|
'inventory' => 1,
|
|
|
|
|
|
'weight' => 0,
|
|
|
|
|
|
'volume' => 0,
|
|
|
|
|
|
'coding' => '',
|
|
|
|
|
|
'barcode' => '',
|
|
|
|
|
|
'add_time' => $now,
|
|
|
|
|
|
]);
|
2026-04-15 11:58:48 +00:00
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$valueBatch[] = ['goods_id' => $goodsId, 'goods_spec_base_id' => $baseId, 'type' => $typeVenue, 'value' => $venueName, 'add_time' => $now];
|
|
|
|
|
|
$valueBatch[] = ['goods_id' => $goodsId, 'goods_spec_base_id' => $baseId, 'type' => $typeZone, 'value' => $s['room'] . '-' . $s['zone'], 'add_time' => $now];
|
|
|
|
|
|
$valueBatch[] = ['goods_id' => $goodsId, 'goods_spec_base_id' => $baseId, 'type' => $typeTime, 'value' => '不限时段', 'add_time' => $now];
|
|
|
|
|
|
$valueBatch[] = ['goods_id' => $goodsId, 'goods_spec_base_id' => $baseId, 'type' => $typeSeat, 'value' => $s['label'], 'add_time' => $now];
|
2026-04-15 11:58:48 +00:00
|
|
|
|
|
2026-04-15 14:02:03 +00:00
|
|
|
|
$specBaseIdMap[$seatId] = [
|
2026-04-16 16:46:00 +00:00
|
|
|
|
'spec_base_id' => $baseId,
|
|
|
|
|
|
'room' => $s['room'],
|
|
|
|
|
|
'zone' => $s['zone'],
|
|
|
|
|
|
'row' => $s['row'],
|
|
|
|
|
|
'col' => $s['col']
|
2026-04-15 14:02:03 +00:00
|
|
|
|
];
|
2026-04-16 16:46:00 +00:00
|
|
|
|
|
2026-04-15 11:58:48 +00:00
|
|
|
|
$generatedCount++;
|
2026-04-16 16:46:00 +00:00
|
|
|
|
|
|
|
|
|
|
if (count($valueBatch) >= self::BATCH_SIZE) {
|
|
|
|
|
|
hinkacade\Db::name('GoodsSpecValue')->insertAll($valueBatch);
|
|
|
|
|
|
$valueBatch = [];
|
|
|
|
|
|
}
|
2026-04-15 11:58:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
if (!empty($valueBatch)) {
|
|
|
|
|
|
hinkacade\Db::name('GoodsSpecValue')->insertAll($valueBatch);
|
2026-04-15 11:58:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
hinkacade\Db::commit();
|
2026-04-15 11:58:48 +00:00
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
return [
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'msg' => '生成成功',
|
|
|
|
|
|
'data' => [
|
|
|
|
|
|
'total' => count($seatsToInsert),
|
|
|
|
|
|
'generated' => $generatedCount,
|
|
|
|
|
|
'spec_base_id_map' => $specBaseIdMap,
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
2026-04-15 11:58:48 +00:00
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
hinkacade\Db::rollback();
|
|
|
|
|
|
return ['code' => -99, 'msg' => '事务异常:' . $e->getMessage()];
|
|
|
|
|
|
}
|
2026-04-15 11:58:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
private static function ensureVrSpecTypes(int $goodsId, string $venueName): array
|
2026-04-15 11:58:48 +00:00
|
|
|
|
{
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$types = ['$vr-场馆', '$vr-分区', '$vr-时段', '$vr-座位号'];
|
|
|
|
|
|
$typeIds = [];
|
2026-04-15 11:58:48 +00:00
|
|
|
|
$now = time();
|
|
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$existing = hinkacade\Db::name('GoodsSpecType')
|
|
|
|
|
|
->where('goods_id', $goodsId)
|
|
|
|
|
|
->where('name', 'in', $types)
|
|
|
|
|
|
->column('id', 'name');
|
2026-04-15 11:58:48 +00:00
|
|
|
|
|
2026-04-16 16:46:00 +00:00
|
|
|
|
foreach ($types as $name) {
|
|
|
|
|
|
if (isset($existing[$name])) {
|
|
|
|
|
|
$typeIds[$name] = $existing[$name];
|
2026-04-15 11:58:48 +00:00
|
|
|
|
} else {
|
2026-04-16 16:46:00 +00:00
|
|
|
|
$val = '';
|
|
|
|
|
|
if ($name === '$vr-场馆') $val = $venueName;
|
|
|
|
|
|
if ($name === '$vr-时段') $val = '不限时段';
|
|
|
|
|
|
|
|
|
|
|
|
$id = hinkacade\Db::name('GoodsSpecType')->insertGetId([
|
2026-04-15 11:58:48 +00:00
|
|
|
|
'goods_id' => $goodsId,
|
|
|
|
|
|
'name' => $name,
|
2026-04-16 16:46:00 +00:00
|
|
|
|
'value' => $val,
|
2026-04-15 11:58:48 +00:00
|
|
|
|
'add_time' => $now,
|
|
|
|
|
|
]);
|
2026-04-16 16:46:00 +00:00
|
|
|
|
if (!$id) {
|
|
|
|
|
|
return ['code' => -10, 'msg' => "写入规格类型 {$name} 失败"];
|
|
|
|
|
|
}
|
2026-04-15 11:58:48 +00:00
|
|
|
|
$typeIds[$name] = $id;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ['code' => 0, 'data' => $typeIds];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|