star-cloud/app/Services/Product/ProductCatalogService.php
sky121113 2c8a2de81c [FEAT] 實作商品主檔 B012 Redis 快取機制 (Phase 1)
1. 新增 ProductCatalogService 負責商品清單轉換與 Redis 快取邏輯。
2. 新增 WarmProductsCacheCommand (artisan products:warm-cache) 用於預熱快取。
3. 修改 ProductController 在商品異動時自動重建快取。
4. 修改 MachineController@getProducts (B012) 改為 Cache-First 讀取。
5. 在 Company 模型補上 products 關聯以支援暖機指令。
2026-05-13 18:51:35 +08:00

106 lines
3.0 KiB
PHP

<?php
namespace App\Services\Product;
use App\Models\Product\Product;
use Illuminate\Support\Facades\Cache;
class ProductCatalogService
{
/**
* Get the Redis cache key for a company's product catalog.
*
* @param int $companyId
* @return string
*/
public function getCacheKey(int $companyId): string
{
return "products_catalog:{$companyId}";
}
/**
* Get the product catalog payload, using cache if available.
*
* @param int $companyId
* @return array
*/
public function getPayload(int $companyId): array
{
$cacheKey = $this->getCacheKey($companyId);
$cached = Cache::get($cacheKey);
if ($cached) {
return $cached;
}
return $this->rebuildCache($companyId);
}
/**
* Rebuild the product catalog cache for a company.
*
* @param int $companyId
* @return array
*/
public function rebuildCache(int $companyId): array
{
$products = Product::where('company_id', $companyId)
->with(['translations'])
->active()
->get()
->map(fn($p) => $this->mapProduct($p));
$payload = [
'success' => true,
'code' => 200,
'data' => $products->toArray(),
];
Cache::forever($this->getCacheKey($companyId), $payload);
return $payload;
}
/**
* Invalidate the product catalog cache for a company.
*
* @param int $companyId
* @return void
*/
public function invalidate(int $companyId): void
{
Cache::forget($this->getCacheKey($companyId));
}
/**
* Map a Product model to the catalog format expected by machines.
*
* @param Product $product
* @return array
*/
private function mapProduct($product): array
{
$nameEn = $product->translations->firstWhere('locale', 'en')?->value ?? '';
$nameJp = $product->translations->firstWhere('locale', 'ja')?->value ?? '';
return [
't060v00' => (string) $product->id,
't060v01' => $product->name,
't060v01_en' => $nameEn,
't060v01_jp' => $nameJp,
't060v03' => $product->spec ?? '',
't060v06' => $product->image_url
? (str_starts_with($product->image_url, 'http') ? $product->image_url : asset($product->image_url))
: '',
't060v09' => (float) $product->price,
't060v11' => (int) ($product->track_limit ?? 10),
't060v30' => (float) ($product->member_price ?? $product->price),
't060v40' => $product->metadata['marketing_plan'] ?? '',
't060v41' => $product->metadata['material_code'] ?? $product->barcode ?? '',
'spring_limit' => (int) ($product->spring_limit ?? 10),
'track_limit' => (int) ($product->track_limit ?? 10),
't063v03' => (float) $product->price,
];
}
}