feat(integration): 完善外部 API 對接邏輯與安全性
1. 新增 API Rate Limiting (每分鐘 60 次) 2. 實作 ProductServiceInterface 與 findOrCreateWarehouseByName 解決跨模組耦合問題 3. 強化 OrderSync API 驗證 (price 欄位限制最小 0、payment_method 加上允許白名單) 4. 實作 OrderSync API 冪等性處理,重複訂單直接回傳現有資訊 5. 修正 ProductSync API 同步邏輯,每次同步皆會更新產品分類與單位 6. 完善 integration API 對接手冊內容與 UI 排版
This commit is contained in:
@@ -131,4 +131,12 @@ interface InventoryServiceInterface
|
||||
* @return array
|
||||
*/
|
||||
public function getDashboardStats(): array;
|
||||
|
||||
/**
|
||||
* 依倉庫名稱查找或建立倉庫(供外部整合用)。
|
||||
*
|
||||
* @param string $warehouseName
|
||||
* @return object
|
||||
*/
|
||||
public function findOrCreateWarehouseByName(string $warehouseName);
|
||||
}
|
||||
25
app/Modules/Inventory/Contracts/ProductServiceInterface.php
Normal file
25
app/Modules/Inventory/Contracts/ProductServiceInterface.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Inventory\Contracts;
|
||||
|
||||
/**
|
||||
* 產品服務介面 — 供跨模組使用(如 Integration 模組)。
|
||||
*/
|
||||
interface ProductServiceInterface
|
||||
{
|
||||
/**
|
||||
* 透過外部 POS ID 進行產品新增或更新(Upsert)。
|
||||
*
|
||||
* @param array $data
|
||||
* @return object
|
||||
*/
|
||||
public function upsertFromPos(array $data);
|
||||
|
||||
/**
|
||||
* 透過外部 POS ID 查找產品。
|
||||
*
|
||||
* @param string $externalPosId
|
||||
* @return object|null
|
||||
*/
|
||||
public function findByExternalPosId(string $externalPosId);
|
||||
}
|
||||
@@ -4,13 +4,16 @@ namespace App\Modules\Inventory;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use App\Modules\Inventory\Contracts\InventoryServiceInterface;
|
||||
use App\Modules\Inventory\Contracts\ProductServiceInterface;
|
||||
use App\Modules\Inventory\Services\InventoryService;
|
||||
use App\Modules\Inventory\Services\ProductService;
|
||||
|
||||
class InventoryServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->bind(InventoryServiceInterface::class, InventoryService::class);
|
||||
$this->app->bind(ProductServiceInterface::class, ProductService::class);
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
|
||||
@@ -590,5 +590,22 @@ class InventoryService implements InventoryServiceInterface
|
||||
'abnormalItems' => $abnormalItems,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 依倉庫名稱查找或建立倉庫(供外部整合用)。
|
||||
*
|
||||
* @param string $warehouseName
|
||||
* @return Warehouse
|
||||
*/
|
||||
public function findOrCreateWarehouseByName(string $warehouseName)
|
||||
{
|
||||
return Warehouse::firstOrCreate(
|
||||
['name' => $warehouseName],
|
||||
[
|
||||
'code' => 'SALES-' . strtoupper(bin2hex(random_bytes(4))),
|
||||
'type' => 'system_sales',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
namespace App\Modules\Inventory\Services;
|
||||
|
||||
use App\Modules\Inventory\Contracts\ProductServiceInterface;
|
||||
use App\Modules\Inventory\Models\Product;
|
||||
use App\Modules\Inventory\Models\Category;
|
||||
use App\Modules\Inventory\Models\Unit;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ProductService
|
||||
class ProductService implements ProductServiceInterface
|
||||
{
|
||||
/**
|
||||
* Upsert product from external POS source.
|
||||
@@ -45,8 +46,8 @@ class ProductService
|
||||
$product->code = $data['code'] ?? $product->external_pos_id;
|
||||
}
|
||||
|
||||
// Handle Category (Default: 未分類)
|
||||
if (empty($product->category_id)) {
|
||||
// Handle Category — 每次同步都更新(若有傳入)
|
||||
if (!empty($data['category']) || empty($product->category_id)) {
|
||||
$categoryName = $data['category'] ?? '未分類';
|
||||
$category = Category::firstOrCreate(
|
||||
['name' => $categoryName],
|
||||
@@ -55,8 +56,8 @@ class ProductService
|
||||
$product->category_id = $category->id;
|
||||
}
|
||||
|
||||
// Handle Base Unit (Default: 個)
|
||||
if (empty($product->base_unit_id)) {
|
||||
// Handle Base Unit — 每次同步都更新(若有傳入)
|
||||
if (!empty($data['unit']) || empty($product->base_unit_id)) {
|
||||
$unitName = $data['unit'] ?? '個';
|
||||
$unit = Unit::firstOrCreate(['name' => $unitName]);
|
||||
$product->base_unit_id = $unit->id;
|
||||
@@ -69,4 +70,15 @@ class ProductService
|
||||
return $product;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 透過外部 POS ID 查找產品。
|
||||
*
|
||||
* @param string $externalPosId
|
||||
* @return Product|null
|
||||
*/
|
||||
public function findByExternalPosId(string $externalPosId)
|
||||
{
|
||||
return Product::where('external_pos_id', $externalPosId)->first();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user