1. 新增 ProductCatalogService 負責商品清單轉換與 Redis 快取邏輯。 2. 新增 WarmProductsCacheCommand (artisan products:warm-cache) 用於預熱快取。 3. 修改 ProductController 在商品異動時自動重建快取。 4. 修改 MachineController@getProducts (B012) 改為 Cache-First 讀取。 5. 在 Company 模型補上 products 關聯以支援暖機指令。
106 lines
3.0 KiB
PHP
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,
|
|
];
|
|
}
|
|
}
|