feat(seatmap-api): 新增 SeatMapService + seatmap action + 支付后清除缓存
- SeatMapService.php: GetSeatMap() 含实时 inventory,模板快照 Cache TTL 60s - api/Goods.php: 新增 seatmap action,调用 SeatMapService::GetSeatMap() - TicketService.php: onOrderPaid 成功后调用 SeatMapService::ClearCache() - 座位图 API: GET /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=seatmap&goods_id=Xfeat/b-verification-clean
parent
ff5e80df22
commit
d0176eeeff
|
|
@ -0,0 +1,300 @@
|
|||
<?php
|
||||
/**
|
||||
* VR票务插件 - C端商品API控制器
|
||||
*
|
||||
* 路由: /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=xxx
|
||||
*
|
||||
* @package vr_ticket\api
|
||||
*/
|
||||
|
||||
namespace app\plugins\vr_ticket\api;
|
||||
|
||||
use app\plugins\vr_ticket\service\SeatMapService;
|
||||
use app\service\GoodsService;
|
||||
|
||||
/**
|
||||
* C端商品 API
|
||||
*/
|
||||
class Goods
|
||||
{
|
||||
private static function success($data = [], string $msg = 'success')
|
||||
{
|
||||
return [
|
||||
'code' => 0,
|
||||
'msg' => $msg,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
private static function error(string $msg = '请求失败', int $code = -1)
|
||||
{
|
||||
return [
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取热门推荐商品
|
||||
*
|
||||
* GET /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=recommend
|
||||
*/
|
||||
public function recommend()
|
||||
{
|
||||
try {
|
||||
// 调用 ShopXO 商品服务获取热门商品
|
||||
// VR票务插件的 category_id 需要在商品管理中设置
|
||||
$params = [
|
||||
'is_new' => 0,
|
||||
'is_recommend' => 1,
|
||||
'is_error' => 0,
|
||||
'is_delete_time' => 0,
|
||||
'start' => 0,
|
||||
'num' => 10,
|
||||
'order_by' => 'sales',
|
||||
'sort' => 'desc',
|
||||
];
|
||||
|
||||
$result = GoodsService::GoodsList($params);
|
||||
$list = self::formatGoodsList($result);
|
||||
|
||||
return self::success([
|
||||
'list' => $list,
|
||||
'count' => count($list),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return self::error('获取推荐失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
*
|
||||
* GET /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=lists
|
||||
* @param int city_id 城市ID筛选
|
||||
* @param int page 页码
|
||||
* @param int size 每页数量
|
||||
*/
|
||||
public function lists()
|
||||
{
|
||||
try {
|
||||
$page = input('page', 1, 'intval');
|
||||
$size = input('size', 10, 'intval');
|
||||
$cityId = input('city_id', 0, 'intval');
|
||||
if (empty($cityId)) {
|
||||
$cityId = input('cityid', 0, 'intval');
|
||||
}
|
||||
|
||||
$start = ($page - 1) * $size;
|
||||
|
||||
$params = [
|
||||
'is_new' => 0,
|
||||
'is_error' => 0,
|
||||
'is_delete_time' => 0,
|
||||
'start' => $start,
|
||||
'num' => $size,
|
||||
'order_by' => 'add_time',
|
||||
'sort' => 'desc',
|
||||
];
|
||||
|
||||
// 城市筛选(如果有设置produce_region)
|
||||
if (!empty($cityId)) {
|
||||
$params['produce_region'] = $cityId;
|
||||
}
|
||||
|
||||
$result = GoodsService::GoodsList($params);
|
||||
$list = self::formatGoodsList($result);
|
||||
|
||||
return self::success([
|
||||
'list' => $list,
|
||||
'count' => count($list),
|
||||
'page' => $page,
|
||||
'size' => $size,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return self::error('获取列表失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取周边商品
|
||||
*
|
||||
* GET /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=merchandise
|
||||
*/
|
||||
public function merchandise()
|
||||
{
|
||||
try {
|
||||
$page = input('page', 1, 'intval');
|
||||
$size = input('size', 20, 'intval');
|
||||
|
||||
$start = ($page - 1) * $size;
|
||||
|
||||
// 获取VR票务相关的周边商品(非票务类型)
|
||||
$params = [
|
||||
'is_new' => 0,
|
||||
'is_error' => 0,
|
||||
'is_delete_time' => 0,
|
||||
'start' => $start,
|
||||
'num' => $size,
|
||||
'order_by' => 'sales',
|
||||
'sort' => 'desc',
|
||||
// 可以根据实际情况添加商品分类筛选
|
||||
];
|
||||
|
||||
$result = GoodsService::GoodsList($params);
|
||||
$list = self::formatGoodsList($result);
|
||||
|
||||
return self::success([
|
||||
'list' => $list,
|
||||
'count' => count($list),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return self::error('获取周边商品失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取商品详情
|
||||
*
|
||||
* GET /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=detail&id=X
|
||||
*/
|
||||
public function detail()
|
||||
{
|
||||
$goodsId = input('id', 0, 'intval');
|
||||
if ($goodsId <= 0) {
|
||||
return self::error('参数错误:商品ID无效');
|
||||
}
|
||||
|
||||
try {
|
||||
$goods = GoodsService::GoodsDetail($goodsId);
|
||||
|
||||
if (empty($goods)) {
|
||||
return self::error('商品不存在', -404);
|
||||
}
|
||||
|
||||
return self::success([
|
||||
'goods' => self::formatGoodsDetail($goods),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return self::error('获取详情失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索商品
|
||||
*
|
||||
* GET /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=search&keyword=X
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
$keyword = input('keyword', '', 'trim');
|
||||
$page = input('page', 1, 'intval');
|
||||
$size = input('size', 10, 'intval');
|
||||
|
||||
if (empty($keyword)) {
|
||||
return self::error('请输入搜索关键词');
|
||||
}
|
||||
|
||||
try {
|
||||
$start = ($page - 1) * $size;
|
||||
|
||||
$params = [
|
||||
'is_new' => 0,
|
||||
'is_error' => 0,
|
||||
'is_delete_time' => 0,
|
||||
'start' => $start,
|
||||
'num' => $size,
|
||||
'title_like' => $keyword,
|
||||
'order_by' => 'sales',
|
||||
'sort' => 'desc',
|
||||
];
|
||||
|
||||
$result = GoodsService::GoodsList($params);
|
||||
$list = self::formatGoodsList($result);
|
||||
|
||||
return self::success([
|
||||
'list' => $list,
|
||||
'count' => count($list),
|
||||
'keyword' => $keyword,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return self::error('搜索失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取座位图(含实时库存)
|
||||
*
|
||||
* GET /api.php?s=plugins/index&pluginsname=vr_ticket&pluginscontrol=goods&pluginsaction=seatmap&goods_id=118
|
||||
*
|
||||
* @return array { code, msg, data: { seatSpecMap, goods_spec_data } }
|
||||
*/
|
||||
public function seatmap()
|
||||
{
|
||||
$goodsId = input('goods_id', 0, 'intval');
|
||||
if ($goodsId <= 0) {
|
||||
return self::error('参数错误:goods_id 无效');
|
||||
}
|
||||
|
||||
try {
|
||||
$data = SeatMapService::GetSeatMap($goodsId);
|
||||
return self::success($data);
|
||||
} catch (\Exception $e) {
|
||||
return self::error('获取座位图失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化商品列表数据
|
||||
*/
|
||||
private static function formatGoodsList($result)
|
||||
{
|
||||
$list = [];
|
||||
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $goods) {
|
||||
$list[] = [
|
||||
'id' => $goods['id'],
|
||||
'title' => $goods['title'],
|
||||
'image' => $goods['image'],
|
||||
'price' => $goods['price'],
|
||||
'original_price' => $goods['original_price'] ?? $goods['price'],
|
||||
'sales' => $goods['sales'] ?? 0,
|
||||
'stock' => $goods['stock'] ?? 0,
|
||||
'venue' => isset($goods['produce_venue']) ? $goods['produce_venue'] : '',
|
||||
'date' => isset($goods['produce_date']) ? $goods['produce_date'] : '',
|
||||
'add_time' => $goods['add_time'] ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化商品详情数据
|
||||
*/
|
||||
private static function formatGoodsDetail($goods)
|
||||
{
|
||||
return [
|
||||
'id' => $goods['id'],
|
||||
'title' => $goods['title'],
|
||||
'image' => $goods['image'],
|
||||
'images' => !empty($goods['images']) ? explode(',', $goods['images']) : [$goods['image']],
|
||||
'price' => $goods['price'],
|
||||
'original_price' => $goods['original_price'] ?? $goods['price'],
|
||||
'sales' => $goods['sales'] ?? 0,
|
||||
'stock' => $goods['stock'] ?? 0,
|
||||
'content' => htmlspecialchars_decode($goods['content'] ?? ''),
|
||||
'spec_type' => $goods['spec_type'] ?? 0,
|
||||
'spec_value_id' => $goods['spec_value_id'] ?? '',
|
||||
// 票务相关字段
|
||||
'venue' => $goods['produce_venue'] ?? '',
|
||||
'date' => $goods['produce_date'] ?? '',
|
||||
'time' => $goods['produce_time'] ?? '',
|
||||
'region' => $goods['produce_region'] ?? '',
|
||||
'add_time' => $goods['add_time'] ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
<?php
|
||||
namespace app\plugins\vr_ticket\service;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* VR票务 - 座位图服务(UniApp seatmap API 专用)
|
||||
*
|
||||
* 提供 seatSpecMap(含实时库存)+ goods_spec_data
|
||||
* 模板快照走 ShopXO Cache(TTL 60s),库存实时读 DB
|
||||
*
|
||||
* @package vr_ticket\service
|
||||
*/
|
||||
class SeatMapService
|
||||
{
|
||||
/**
|
||||
* 缓存 key 前缀
|
||||
*/
|
||||
const CACHE_KEY_PREFIX = 'vr_seatmap_';
|
||||
|
||||
/**
|
||||
* 模板快照缓存 TTL(秒)
|
||||
*/
|
||||
const CACHE_TTL = 60;
|
||||
|
||||
/**
|
||||
* 获取座位图完整数据(含实时库存)
|
||||
*
|
||||
* @param int $goodsId
|
||||
* @return array [
|
||||
* 'seatSpecMap' => [...], // seatKey → {inventory, price, spec, ...}
|
||||
* 'goods_spec_data' => [...], // 场次列表(含最低价)
|
||||
* ]
|
||||
*/
|
||||
public static function GetSeatMap(int $goodsId): array
|
||||
{
|
||||
// 1. 读取 vr_goods_config(数组,取第一项)
|
||||
$vrConfigRaw = Db::name('Goods')
|
||||
->where('id', $goodsId)
|
||||
->value('vr_goods_config');
|
||||
|
||||
$configs = json_decode($vrConfigRaw ?? '', true);
|
||||
if (empty($configs) || !is_array($configs)) {
|
||||
return ['seatSpecMap' => [], 'goods_spec_data' => []];
|
||||
}
|
||||
$config = $configs[0];
|
||||
|
||||
$templateId = intval($config['template_id'] ?? 0);
|
||||
if ($templateId <= 0) {
|
||||
return ['seatSpecMap' => [], 'goods_spec_data' => []];
|
||||
}
|
||||
|
||||
// 2. 获取座位模板(含 rooms[] / sections[] / map[])
|
||||
$seatTemplate = self::getSeatTemplate($templateId);
|
||||
if (empty($seatTemplate)) {
|
||||
return ['seatSpecMap' => [], 'goods_spec_data' => []];
|
||||
}
|
||||
|
||||
// 3. 构建 seatSpecMap(含 inventory,实时读 DB,包含已售)
|
||||
$seatSpecMap = self::buildSeatSpecMap($goodsId, $seatTemplate);
|
||||
|
||||
// 4. 构建场次列表(从 sessions[] + seatSpecMap 合并最低价)
|
||||
$goodsSpecData = self::buildGoodsSpecData($config['sessions'] ?? [], $seatSpecMap);
|
||||
|
||||
return [
|
||||
'seatSpecMap' => $seatSpecMap,
|
||||
'goods_spec_data' => $goodsSpecData,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除座位图缓存(订单支付成功后调用)
|
||||
*
|
||||
* @param int $goodsId
|
||||
* @return bool
|
||||
*/
|
||||
public static function ClearCache(int $goodsId): bool
|
||||
{
|
||||
return \think\facade\Cache::delete(self::CACHE_KEY_PREFIX . $goodsId);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────
|
||||
// 私有方法
|
||||
// ─────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* 获取座位模板(走 ShopXO Cache,TTL 60s)
|
||||
*
|
||||
* @param int $templateId
|
||||
* @return array|null
|
||||
*/
|
||||
private static function getSeatTemplate(int $templateId)
|
||||
{
|
||||
$cacheKey = self::CACHE_KEY_PREFIX . $templateId;
|
||||
$cached = \think\facade\Cache::get($cacheKey);
|
||||
if ($cached !== null) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
$row = Db::name('vr_seat_templates')->find($templateId);
|
||||
if (empty($row)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$seatMap = json_decode($row['seat_map'] ?? '{}', true);
|
||||
if (empty($seatMap)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 缓存(TTL = self::CACHE_TTL)
|
||||
\think\facade\Cache::set($cacheKey, $seatMap, self::CACHE_TTL);
|
||||
|
||||
return $seatMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建座位规格映射表(含 inventory,实时读 DB)
|
||||
*
|
||||
* 遍历所有 GoodsSpecBase(含 inventory=0 的已售座位),
|
||||
* 与 GoodsSpecType + GoodsSpecValue 关联,
|
||||
* 输出 seatSpecMap。
|
||||
*
|
||||
* @param int $goodsId
|
||||
* @param array $seatTemplate seat_map JSON(已解析)
|
||||
* @return array seatSpecMap
|
||||
*/
|
||||
private static function buildSeatSpecMap(int $goodsId, array $seatTemplate): array
|
||||
{
|
||||
$seatSpecMap = [];
|
||||
|
||||
// 1. 查询当前商品所有 GoodsSpecBase(不过滤 inventory,获取所有座位含已售)
|
||||
$specs = Db::name('GoodsSpecBase')
|
||||
->where('goods_id', $goodsId)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
if (empty($specs)) {
|
||||
return $seatSpecMap;
|
||||
}
|
||||
|
||||
// 2. 查询 GoodsSpecType 获取维度映射(name → index)
|
||||
$specTypes = Db::name('GoodsSpecType')
|
||||
->where('goods_id', $goodsId)
|
||||
->order('id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$dimIndexByName = [];
|
||||
$dimValuesByName = []; // name → [value1, value2, ...]
|
||||
foreach ($specTypes as $idx => $type) {
|
||||
$dimName = $type['name'] ?? '';
|
||||
if (!empty($dimName)) {
|
||||
$dimIndexByName[$dimName] = $idx;
|
||||
$values = json_decode($type['value'] ?? '[]', true);
|
||||
$dimValuesByName[$dimName] = [];
|
||||
foreach ($values as $v) {
|
||||
if (isset($v['name'])) {
|
||||
$dimValuesByName[$dimName][] = $v['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 查询每个 spec_base_id 对应的 GoodsSpecValue
|
||||
$specBaseIds = array_column($specs, 'id');
|
||||
$specValues = Db::name('GoodsSpecValue')
|
||||
->whereIn('goods_spec_base_id', $specBaseIds)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
// 4. 按 spec_base_id 分组,通过值匹配找到维度名
|
||||
$specByBaseId = [];
|
||||
foreach ($specValues as $sv) {
|
||||
$baseId = $sv['goods_spec_base_id'];
|
||||
$value = $sv['value'] ?? '';
|
||||
|
||||
$dimName = '';
|
||||
foreach ($dimValuesByName as $name => $values) {
|
||||
if (in_array($value, $values)) {
|
||||
$dimName = $name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$specByBaseId[$baseId][] = [
|
||||
'type' => $dimName,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
// 5. 解析座位模板中的 room 信息(用于提取 rowLabel, colNum, section 等)
|
||||
$rooms = $seatTemplate['rooms'] ?? [];
|
||||
$roomSeatInfo = []; // roomId → [rowLabel_colNum → [...]]
|
||||
foreach ($rooms as $rIdx => $room) {
|
||||
$roomId = !empty($room['id']) ? $room['id'] : ('room_' . $rIdx);
|
||||
$sections = $room['sections'] ?? [];
|
||||
$map = $room['map'] ?? [];
|
||||
$seatsData = $room['seats'] ?? [];
|
||||
|
||||
foreach ($map as $rowIndex => $rowStr) {
|
||||
$rowLabel = chr(65 + $rowIndex);
|
||||
$chars = preg_split('//u', $rowStr, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($chars as $colIndex => $char) {
|
||||
if ($char === '_' || $char === '-' || !isset($seatsData[$char])) {
|
||||
continue;
|
||||
}
|
||||
$colNum = $colIndex + 1;
|
||||
|
||||
$sectionInfo = null;
|
||||
foreach ($sections as $sec) {
|
||||
if (($sec['char'] ?? '') === $char) {
|
||||
$sectionInfo = $sec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$roomSeatInfo[$roomId][$rowLabel . '_' . $colNum] = [
|
||||
'rowLabel' => $rowLabel,
|
||||
'colNum' => $colNum,
|
||||
'section' => $sectionInfo,
|
||||
'char' => $char,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 构建 seatSpecMap:seatKey → 完整规格
|
||||
foreach ($specs as $spec) {
|
||||
$extends = json_decode($spec['extends'] ?? '{}', true);
|
||||
$seatKey = $extends['seat_key'] ?? '';
|
||||
if (empty($seatKey)) continue;
|
||||
|
||||
// 解析 seatKey 格式:roomId_rowLabel_colNum
|
||||
$parts = explode('_', $seatKey);
|
||||
if (count($parts) < 3) continue;
|
||||
$roomId = $parts[0];
|
||||
$rowLabel = $parts[1];
|
||||
$colNum = intval($parts[2]);
|
||||
|
||||
// 提取各维度值
|
||||
$venueName = '';
|
||||
$sectionName = '';
|
||||
$seatName = '';
|
||||
$sessionName = '';
|
||||
$roomName = '';
|
||||
foreach ($specByBaseId[$spec['id']] ?? [] as $specItem) {
|
||||
$specType = $specItem['type'] ?? '';
|
||||
$specVal = $specItem['value'] ?? '';
|
||||
switch ($specType) {
|
||||
case '$vr-场次':
|
||||
$sessionName = $specVal;
|
||||
break;
|
||||
case '$vr-场馆':
|
||||
$venueName = $specVal;
|
||||
break;
|
||||
case '$vr-演播室':
|
||||
$roomName = $specVal;
|
||||
break;
|
||||
case '$vr-分区':
|
||||
$sectionName = $specVal;
|
||||
break;
|
||||
case '$vr-座位号':
|
||||
$seatName = $specVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$seatMeta = $roomSeatInfo[$roomId][$rowLabel . '_' . $colNum] ?? [
|
||||
'rowLabel' => $rowLabel,
|
||||
'colNum' => $colNum,
|
||||
'section' => null,
|
||||
'char' => '',
|
||||
];
|
||||
|
||||
$seatSpecMap[$seatKey] = [
|
||||
'spec_base_id' => intval($spec['id']),
|
||||
'price' => floatval($spec['price']),
|
||||
'inventory' => intval($spec['inventory']), // ← 关键字段(0=已售)
|
||||
'spec' => $specByBaseId[$spec['id']] ?? [],
|
||||
'rowLabel' => $seatMeta['rowLabel'],
|
||||
'colNum' => $seatMeta['colNum'],
|
||||
'roomId' => $roomId,
|
||||
'roomName' => $roomName,
|
||||
'section' => $seatMeta['section'],
|
||||
'venueName' => $venueName,
|
||||
'sectionName' => $sectionName,
|
||||
'seatName' => $seatName,
|
||||
'sessionName' => $sessionName,
|
||||
];
|
||||
}
|
||||
|
||||
return $seatSpecMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 goods_spec_data(场次列表,含最低价)
|
||||
*
|
||||
* @param array $sessions vr_goods_config.sessions[]
|
||||
* @param array $seatSpecMap
|
||||
* @return array
|
||||
*/
|
||||
private static function buildGoodsSpecData(array $sessions, array $seatSpecMap): array
|
||||
{
|
||||
if (empty($sessions)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($sessions as $session) {
|
||||
$specName = ($session['start'] ?? '') . '-' . ($session['end'] ?? '');
|
||||
$minPrice = PHP_FLOAT_MAX;
|
||||
|
||||
// 从 seatSpecMap 中找该场次的最低价
|
||||
foreach ($seatSpecMap as $info) {
|
||||
if (($info['sessionName'] ?? '') === $specName) {
|
||||
$p = $info['price'] ?? PHP_FLOAT_MAX;
|
||||
if ($p < $minPrice) {
|
||||
$minPrice = $p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'spec_name' => $specName,
|
||||
'price' => $minPrice < PHP_FLOAT_MAX ? $minPrice : 0,
|
||||
'start' => $session['start'] ?? '',
|
||||
'end' => $session['end'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -121,6 +121,11 @@ class TicketService extends BaseService
|
|||
'tickets_issued' => $count,
|
||||
]);
|
||||
|
||||
// 清除座位图缓存(确保下一位买家看到最新库存)
|
||||
if ($count > 0) {
|
||||
SeatMapService::ClearCache(intval($goodsId));
|
||||
}
|
||||
|
||||
return $count > 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue