vr-shopxo-plugin/shopxo/app/plugins/vr_ticket/service/SeatSkuService.php

244 lines
9.1 KiB
PHP
Raw Normal View History

<?php
/**
* VR票务插件 - 座位 SKU 服务
*
* 核心业务:批量生成座位级 SKUspec_base + spec_value
* 旁路 GoodsSpecificationsInsert(),直接 SQL INSERT
*
* @package vr_ticket\service
*/
namespace app\plugins r_ticket\service;
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. 加载座位模板
$template = hink acade\Db::name(self::table('seat_templates'))
->where('id', $seatTemplateId)
->find();
if (empty($template)) {
return ['code' => -2, 'msg' => "座位模板 {$seatTemplateId} 不存在"];
}
// 2. 解析 seat_map
$seatMap = json_decode($template['seat_map'] ?? '{}', true);
$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 数据无效'];
}
}
$venueName = $seatMap['venue']['name'] ?? $template['name'] ?? '未命名场馆';
$specTypeIds = self::ensureVrSpecTypes($goodsId, $venueName);
if ($specTypeIds['code'] !== 0) {
return $specTypeIds;
}
$typeVenue = $specTypeIds['data']['$vr-场馆'];
$typeZone = $specTypeIds['data']['$vr-分区'];
$typeTime = $specTypeIds['data']['$vr-时段']; // 时段留作可选,前端默认传“不限时段”
$typeSeat = $specTypeIds['data']['$vr-座位号'];
$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
];
}
}
}
if (empty($seatsToInsert)) {
return ['code' => -3, 'msg' => '无有效座位可生成'];
}
// 查询已有 SKU
$existingBases = hink acade\Db::name('GoodsSpecBase')
->where('goods_id', $goodsId)
->column('id', 'id');
$existingValues = [];
if (!empty($existingBases)) {
$valueRows = hink acade\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;
}
}
$now = time();
$generatedCount = 0;
$specBaseIdMap = [];
hink acade\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; // 已经存在,查出来返回映射即可
}
$baseId = hink acade\Db::name('GoodsSpecBase')->insertGetId([
'goods_id' => $goodsId,
'price' => $s['price'],
'inventory' => 1,
'weight' => 0,
'volume' => 0,
'coding' => '',
'barcode' => '',
'add_time' => $now,
]);
$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];
$specBaseIdMap[$seatId] = [
'spec_base_id' => $baseId,
'room' => $s['room'],
'zone' => $s['zone'],
'row' => $s['row'],
'col' => $s['col']
];
$generatedCount++;
if (count($valueBatch) >= self::BATCH_SIZE) {
hink acade\Db::name('GoodsSpecValue')->insertAll($valueBatch);
$valueBatch = [];
}
}
if (!empty($valueBatch)) {
hink acade\Db::name('GoodsSpecValue')->insertAll($valueBatch);
}
hink acade\Db::commit();
return [
'code' => 0,
'msg' => '生成成功',
'data' => [
'total' => count($seatsToInsert),
'generated' => $generatedCount,
'spec_base_id_map' => $specBaseIdMap,
]
];
} catch (\Exception $e) {
hink acade\Db::rollback();
return ['code' => -99, 'msg' => '事务异常:' . $e->getMessage()];
}
}
private static function ensureVrSpecTypes(int $goodsId, string $venueName): array
{
$types = ['$vr-场馆', '$vr-分区', '$vr-时段', '$vr-座位号'];
$typeIds = [];
$now = time();
$existing = hink acade\Db::name('GoodsSpecType')
->where('goods_id', $goodsId)
->where('name', 'in', $types)
->column('id', 'name');
foreach ($types as $name) {
if (isset($existing[$name])) {
$typeIds[$name] = $existing[$name];
} else {
$val = '';
if ($name === '$vr-场馆') $val = $venueName;
if ($name === '$vr-时段') $val = '不限时段';
$id = hink acade\Db::name('GoodsSpecType')->insertGetId([
'goods_id' => $goodsId,
'name' => $name,
'value' => $val,
'add_time' => $now,
]);
if (!$id) {
return ['code' => -10, 'msg' => "写入规格类型 {$name} 失败"];
}
$typeIds[$name] = $id;
}
}
return ['code' => 0, 'data' => $typeIds];
}
}