263 lines
10 KiB
PHP
263 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Customer;
|
|
use App\Models\Order;
|
|
use App\Models\Product;
|
|
use App\Models\Withdraw;
|
|
use App\Services\ConfigService;
|
|
use App\Services\WalletBroadcastService;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
|
use Rooyesh\Wallet\interfaces\WalletInterface;
|
|
use Rooyesh\Wallet\models\Transaction;
|
|
use Rooyesh\Wallet\models\Wallet;
|
|
|
|
class FixMisplacedProductGoldCommand extends Command
|
|
{
|
|
protected $signature = 'wallet:fix-misplaced-product-gold
|
|
{--customer= : شناسه مشتری}
|
|
{--product= : شناسه محصول (مثلاً 40 برای آبشده سرمایهگذاری)}
|
|
{--gold= : مقدار طلا به گرم (اختیاری)}
|
|
{--order= : شناسه سفارش برای تشخیص خودکار مقدار}
|
|
{--transaction= : شناسه تراکنش برای تشخیص خودکار مقدار}
|
|
{--execute : اجرای واقعی (بدون این فلگ فقط پیشنمایش است)}';
|
|
|
|
protected $description = 'انتقال طلای اشتباهینشسته در کیف اصلی به کیف محصول (باگ abshode_id بدون product_id)';
|
|
|
|
public function handle(ConfigService $configService): int
|
|
{
|
|
$customerId = (int) $this->option('customer');
|
|
$productId = (int) $this->option('product');
|
|
$execute = (bool) $this->option('execute');
|
|
|
|
if ($customerId <= 0 || $productId <= 0) {
|
|
$this->error('پارامترهای --customer و --product الزامی هستند.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$customer = Customer::find($customerId);
|
|
if (!$customer) {
|
|
$this->error("مشتری {$customerId} یافت نشد.");
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$product = Product::find($productId);
|
|
if (!$product) {
|
|
$this->error("محصول {$productId} یافت نشد.");
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
if ($configService->isMainWalletProduct($productId)) {
|
|
$this->error("محصول {$productId} به کیف اصلی متصل است؛ نیازی به انتقال نیست.");
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$goldAmount = $this->resolveGoldAmount($customerId, $productId);
|
|
if ($goldAmount === null || $goldAmount <= 0) {
|
|
$this->error('مقدار طلا مشخص نشد. از --gold یا --order یا --transaction استفاده کنید.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$ownerType = (int) $customer->type;
|
|
$mainWallet = Wallet::query()
|
|
->where('owner', $customerId)
|
|
->where('owner_type', $ownerType)
|
|
->whereNull('fund_id')
|
|
->whereNull('product_id')
|
|
->first();
|
|
|
|
if (!$mainWallet) {
|
|
$this->error('کیف اصلی مشتری یافت نشد.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$effectiveProductId = Controller::effectiveProductWalletId($productId);
|
|
$productWallet = Wallet::query()
|
|
->where('owner', $customerId)
|
|
->where('owner_type', $ownerType)
|
|
->whereNull('fund_id')
|
|
->where('product_id', $effectiveProductId)
|
|
->first();
|
|
|
|
$productWalletGold = (float) ($productWallet?->gold ?? 0);
|
|
$mainWalletGold = (float) $mainWallet->gold;
|
|
|
|
$this->info('--- پیشنمایش اصلاح موجودی ---');
|
|
$this->line("مشتری: {$customerId} ({$customer->name})");
|
|
$this->line("محصول: {$productId} ({$product->title})");
|
|
$this->line('مقدار انتقال: ' . round($goldAmount, 5) . ' گرم');
|
|
$this->line('کیف اصلی قبل: ' . round($mainWalletGold, 5) . ' گرم');
|
|
$this->line('کیف محصول قبل: ' . round($productWalletGold, 5) . ' گرم');
|
|
$this->line('کیف اصلی بعد: ' . round($mainWalletGold - $goldAmount, 5) . ' گرم');
|
|
$this->line('کیف محصول بعد: ' . round($productWalletGold + $goldAmount, 5) . ' گرم');
|
|
|
|
if ($mainWalletGold < $goldAmount) {
|
|
$this->error('موجودی کیف اصلی کافی نیست.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
if ($this->alreadyCorrected($customerId, $productId, $goldAmount)) {
|
|
$this->warn('به نظر میرسد این اصلاح قبلاً انجام شده است. برای اطمینان دوباره اجرا نمیشود.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
if (!$execute) {
|
|
$this->newLine();
|
|
$this->warn('حالت پیشنمایش — برای اجرا فلگ --execute را اضافه کنید.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
DB::transaction(function () use ($customer, $customerId, $ownerType, $productId, $product, $goldAmount, $effectiveProductId) {
|
|
$note = 'اصلاح موجودی — انتقال ' . round($goldAmount, 5) . ' گرم از کیف اصلی به کیف «' . $product->title . '"';
|
|
$orderId = (int) ($this->option('order') ?? 0);
|
|
if ($orderId > 0) {
|
|
$note .= " (سفارش {$orderId})";
|
|
}
|
|
|
|
$withdraw = Withdraw::create([
|
|
'user_id' => 0,
|
|
'customer_id' => $customerId,
|
|
'gold' => 0,
|
|
'note' => $note,
|
|
'sts' => 4,
|
|
]);
|
|
|
|
$mainWalletInterface = new WalletInterface($customerId, $ownerType);
|
|
new TransactionInterface($mainWalletInterface, [
|
|
'cash' => 0,
|
|
'gold' => $goldAmount,
|
|
'product_id' => $productId,
|
|
'expire_at' => now(),
|
|
'note' => $note,
|
|
'withdraw_id' => $withdraw->id,
|
|
'order_id' => $orderId > 0 ? $orderId : null,
|
|
'active' => true,
|
|
'title' => 'اصلاح موجودی — کسر از کیف اصلی',
|
|
], 7, false, false);
|
|
|
|
$productWalletInterface = new WalletInterface($customerId, $ownerType, null, $effectiveProductId);
|
|
new TransactionInterface($productWalletInterface, [
|
|
'cash' => 0,
|
|
'gold' => $goldAmount,
|
|
'product_id' => $productId,
|
|
'expire_at' => now(),
|
|
'note' => $note,
|
|
'withdraw_id' => $withdraw->id,
|
|
'order_id' => $orderId > 0 ? $orderId : null,
|
|
'active' => true,
|
|
'title' => 'اصلاح موجودی — واریز به کیف محصول',
|
|
], 7, true, false);
|
|
|
|
app(WalletBroadcastService::class)->broadcastForCustomer($customerId, $ownerType);
|
|
});
|
|
|
|
$this->newLine();
|
|
$this->info('اصلاح با موفقیت انجام شد.');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function resolveGoldAmount(int $customerId, int $productId): ?float
|
|
{
|
|
$explicitGold = $this->option('gold');
|
|
if ($explicitGold !== null && $explicitGold !== '') {
|
|
return round((float) $explicitGold, 5);
|
|
}
|
|
|
|
$transactionId = (int) ($this->option('transaction') ?? 0);
|
|
if ($transactionId > 0) {
|
|
$transaction = Transaction::query()
|
|
->where('id', $transactionId)
|
|
->where('customer_id', $customerId)
|
|
->first();
|
|
|
|
if ($transaction && (float) $transaction->gold > 0) {
|
|
return round(abs((float) $transaction->gold), 5);
|
|
}
|
|
|
|
$this->warn("تراکنش {$transactionId} یافت نشد یا مقدار طلا ندارد.");
|
|
}
|
|
|
|
$orderId = (int) ($this->option('order') ?? 0);
|
|
if ($orderId > 0) {
|
|
$order = Order::query()
|
|
->where('id', $orderId)
|
|
->where('customer_id', $customerId)
|
|
->first();
|
|
|
|
if ($order) {
|
|
$orderProductId = (int) ($order->product_id ?? $order->abshode_id ?? 0);
|
|
if ($orderProductId === $productId && (float) $order->amount > 0) {
|
|
return round((float) $order->amount, 5);
|
|
}
|
|
|
|
$this->warn("سفارش {$orderId} به محصول {$productId} مرتبط نیست یا amount ندارد.");
|
|
} else {
|
|
$this->warn("سفارش {$orderId} یافت نشد.");
|
|
}
|
|
}
|
|
|
|
return $this->detectFromMainWalletTransactions($customerId, $productId);
|
|
}
|
|
|
|
private function detectFromMainWalletTransactions(int $customerId, int $productId): ?float
|
|
{
|
|
$ownerType = (int) (Customer::find($customerId)?->type ?? 2);
|
|
$mainWalletId = Wallet::query()
|
|
->where('owner', $customerId)
|
|
->where('owner_type', $ownerType)
|
|
->whereNull('fund_id')
|
|
->whereNull('product_id')
|
|
->value('id');
|
|
|
|
if (!$mainWalletId) {
|
|
return null;
|
|
}
|
|
|
|
$total = Transaction::query()
|
|
->where('wallet', $mainWalletId)
|
|
->where('customer_id', $customerId)
|
|
->where('product_id', $productId)
|
|
->where('type', 1)
|
|
->where('gold', '>', 0)
|
|
->sum('gold');
|
|
|
|
if ($total > 0) {
|
|
$this->line('مقدار از تراکنشهای خرید روی کیف اصلی تشخیص داده شد: ' . round((float) $total, 5) . ' گرم');
|
|
|
|
return round((float) $total, 5);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function alreadyCorrected(int $customerId, int $productId, float $goldAmount): bool
|
|
{
|
|
$orderId = (int) ($this->option('order') ?? 0);
|
|
$needle = $orderId > 0 ? "سفارش {$orderId}" : 'اصلاح موجودی';
|
|
|
|
$exists = Transaction::query()
|
|
->where('customer_id', $customerId)
|
|
->where('product_id', $productId)
|
|
->where('gold', '>=', $goldAmount - 0.00001)
|
|
->where('note', 'like', '%' . $needle . '%')
|
|
->where('title', 'like', '%واریز به کیف محصول%')
|
|
->exists();
|
|
|
|
return $exists;
|
|
}
|
|
}
|