This commit is contained in:
2026-06-13 12:42:42 +03:30
parent f8c96a420f
commit e70fcef962
8 changed files with 264 additions and 7 deletions
@@ -0,0 +1,48 @@
<?php
namespace App\Http\Controllers\Admin;
use App\DB\Te\TableEngine;
use App\Http\Controllers\Controller;
use App\Models\PriceSyncLog;
use App\Services\ConfigService;
use App\Services\ProductPriceSyncService;
use Illuminate\Http\Request;
class PriceSyncLogController extends Controller
{
public function table(Request $request)
{
$records = PriceSyncLog::query()->orderByDesc('id');
TableEngine::create($records, $request->all())
->initSearch(['source', 'source_url'])
->select(['*'])
->export($records);
return response()->json($records);
}
public function show($id)
{
$log = PriceSyncLog::with('items')->findOrFail($id);
return response()->json($log);
}
public function runSync(ProductPriceSyncService $syncService, ConfigService $configService)
{
if (! $configService->isStore()) {
return response()->json(['message' => 'فقط در حالت فروشگاه'], 422);
}
$syncService->syncStorePrices(true);
$log = PriceSyncLog::with('items')->orderByDesc('id')->first();
return response()->json([
'message' => $log ? 'قیمت‌ها به‌روزرسانی شد' : 'تغییری در قیمت محصولات نبود',
'data' => $log,
]);
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class PriceSyncLog extends Model
{
protected $table = 'price_sync_logs';
public $timestamps = false;
protected $fillable = [
'source',
'source_url',
'liquid_gold_price',
'products_updated',
'created_at',
];
protected $casts = [
'liquid_gold_price' => 'integer',
'products_updated' => 'integer',
'created_at' => 'datetime',
];
public function items(): HasMany
{
return $this->hasMany(PriceSyncLogItem::class, 'log_id');
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PriceSyncLogItem extends Model
{
protected $table = 'price_sync_log_items';
public $timestamps = false;
protected $fillable = [
'log_id',
'product_id',
'product_title',
'reading_method',
'source_name',
'base_price',
'base_profit',
'offset_percent',
'stored_price',
'previous_price',
];
protected $casts = [
'product_id' => 'integer',
'base_price' => 'integer',
'base_profit' => 'integer',
'offset_percent' => 'float',
'stored_price' => 'integer',
'previous_price' => 'integer',
];
public function log(): BelongsTo
{
return $this->belongsTo(PriceSyncLog::class, 'log_id');
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace App\Services;
use App\Models\PriceSyncLog;
use App\Models\PriceSyncLogItem;
use Illuminate\Support\Facades\Schema;
class PriceSyncLogService
{
public function isAvailable(): bool
{
return Schema::hasTable('price_sync_logs') && Schema::hasTable('price_sync_log_items');
}
/**
* @param array<int, array<string, mixed>> $items
*/
public function record(string $source, ?string $sourceUrl, int $liquidGoldPrice, array $items): ?PriceSyncLog
{
if (! $this->isAvailable() || $items === []) {
return null;
}
$changedItems = array_values(array_filter(
$items,
static fn (array $item) => (bool) ($item['changed'] ?? false)
));
if ($changedItems === []) {
return null;
}
$log = PriceSyncLog::create([
'source' => $source,
'source_url' => $sourceUrl,
'liquid_gold_price' => $liquidGoldPrice,
'products_updated' => count($changedItems),
'created_at' => now(),
]);
foreach ($changedItems as $item) {
PriceSyncLogItem::create([
'log_id' => $log->id,
'product_id' => $item['product_id'] ?? null,
'product_title' => $item['product_title'] ?? null,
'reading_method' => $item['reading_method'] ?? null,
'source_name' => $item['source_name'] ?? null,
'base_price' => (int) ($item['base_price'] ?? 0),
'base_profit' => (int) ($item['base_profit'] ?? 0),
'offset_percent' => (float) ($item['offset_percent'] ?? 0),
'stored_price' => (int) ($item['new_price'] ?? 0),
'previous_price' => (int) ($item['old_price'] ?? 0),
]);
}
return $log->load('items');
}
}
+18 -3
View File
@@ -26,21 +26,23 @@ class ProductPriceService
: 0.0;
}
public static function syncFromReference(Product $product, int $basePrice, int $baseProfit = 0): void
/** @return array<string, mixed>|null */
public static function syncFromReference(Product $product, int $basePrice, int $baseProfit = 0, ?string $sourceName = null): ?array
{
if (!Product::hasStoredPriceColumns()) {
return;
return null;
}
$offsetPercent = self::offsetPercent($product);
$adjustedPrice = self::adjustedTotal($basePrice, $baseProfit, $offsetPercent);
$oldPrice = (int) ($product->price ?? 0);
if (
$adjustedPrice <= 0
&& Product::hasUseLastPriceIfZeroColumn()
&& ($product->use_last_price_if_zero ?? false)
) {
return;
return null;
}
$data = ['price' => $adjustedPrice];
@@ -50,6 +52,19 @@ class ProductPriceService
}
Product::where('id', $product->id)->update($data);
return [
'product_id' => $product->id,
'product_title' => $product->title,
'reading_method' => $product->reading_method,
'source_name' => $sourceName ?? $product->reading_method,
'base_price' => $basePrice,
'base_profit' => $baseProfit,
'offset_percent' => $offsetPercent,
'old_price' => $oldPrice,
'new_price' => $adjustedPrice,
'changed' => $oldPrice !== $adjustedPrice,
];
}
/** وقتی فقط درصد در ادمین عوض می‌شود (بدون sync جدید) */
+15 -4
View File
@@ -29,8 +29,12 @@ class ProductPriceSyncService
*/
public function syncStorePrices(bool $forceRefresh = true): bool
{
$feed = $this->zaarboxPriceService->cachedPriceList($forceRefresh)
?? $this->cachedLegacyPriceList($forceRefresh);
$zaarboxFeed = $this->zaarboxPriceService->cachedPriceList($forceRefresh);
$feed = $zaarboxFeed ?? $this->cachedLegacyPriceList($forceRefresh);
$source = $zaarboxFeed !== null ? 'zaarbox' : 'legacy';
$sourceUrl = $source === 'zaarbox'
? $this->zaarboxPriceService->apiUrl()
: 'http://212.23.201.173:8381/price_list';
if ($feed === null) {
return false;
@@ -43,6 +47,7 @@ class ProductPriceSyncService
}
$synced = false;
$logItems = [];
foreach ($products as $product) {
$row = $this->zaarboxPriceService->findFeedRow($feed, (string) $product->reading_method);
@@ -53,9 +58,13 @@ class ProductPriceSyncService
$row = $this->zaarboxPriceService->normalizeFeedRow($row);
$basePrice = (int) ($row['price'] ?? 0);
$baseProfit = (int) ($row['profit'] ?? 0);
$sourceName = trim((string) ($row['name'] ?? $product->reading_method));
ProductPriceService::syncFromReference($product, $basePrice, $baseProfit);
$synced = true;
$result = ProductPriceService::syncFromReference($product, $basePrice, $baseProfit, $sourceName);
if ($result !== null) {
$logItems[] = $result;
$synced = true;
}
if ((int) ($product->bubble ?? 0) === 1 && $liquidGoldPrice > 0 && $basePrice > 0) {
$p = Product::find($product->id);
@@ -66,6 +75,8 @@ class ProductPriceSyncService
}
}
app(PriceSyncLogService::class)->record($source, $sourceUrl, $liquidGoldPrice, $logItems);
return $synced;
}
@@ -0,0 +1,45 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
if (! Schema::hasTable('price_sync_logs')) {
Schema::create('price_sync_logs', function (Blueprint $table) {
$table->id();
$table->string('source', 32);
$table->string('source_url', 512)->nullable();
$table->unsignedBigInteger('liquid_gold_price')->default(0);
$table->unsignedSmallInteger('products_updated')->default(0);
$table->timestamp('created_at')->nullable();
});
}
if (! Schema::hasTable('price_sync_log_items')) {
Schema::create('price_sync_log_items', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('log_id');
$table->unsignedBigInteger('product_id')->nullable();
$table->string('product_title', 255)->nullable();
$table->string('reading_method', 128)->nullable();
$table->string('source_name', 255)->nullable();
$table->unsignedBigInteger('base_price')->default(0);
$table->unsignedBigInteger('base_profit')->default(0);
$table->decimal('offset_percent', 8, 2)->default(0);
$table->unsignedBigInteger('stored_price')->default(0);
$table->unsignedBigInteger('previous_price')->default(0);
$table->index('log_id');
});
}
}
public function down(): void
{
Schema::dropIfExists('price_sync_log_items');
Schema::dropIfExists('price_sync_logs');
}
};
+7
View File
@@ -20,6 +20,7 @@ use App\Http\Controllers\Site\OfferCodesController;
use App\Http\Controllers\Admin\SettingsController;
use \App\Http\Controllers\Admin\GiftCardController;
use App\Http\Controllers\Admin\CategoryController;
use App\Http\Controllers\Admin\PriceSyncLogController;
use App\Services\ConfigService;
@@ -112,6 +113,12 @@ Route::middleware('auth:user')->group(function (){
Route::post('price/table', [PriceController::class, 'table']);
Route::post('prices', [PriceController::class, 'priceData']);
Route::post('prices/chart', [PriceController::class, 'chartPrices']);
if (app(ConfigService::class)->isStore()) {
Route::post('price-sync-logs/table', [PriceSyncLogController::class, 'table']);
Route::get('price-sync-logs/{id}', [PriceSyncLogController::class, 'show']);
Route::post('price-sync/run', [PriceSyncLogController::class, 'runSync']);
}
Route::get('zaarbox/products', [PriceController::class, 'zaarboxProducts']);