feat: add manual deposit system, implement Bank model, remove Docker configs, and initialize comprehensive Laravel best practice documentation for AI agents.
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin\Transaction;
|
||||
|
||||
use App\Enums\TransactionStatus;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Log;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log as FacadesLog;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class AdminTransactionController extends Controller
|
||||
{
|
||||
public function pendingCount(): JsonResponse
|
||||
{
|
||||
$count = Transaction::where('type', 'manual')
|
||||
->where('status', TransactionStatus::PENDING->value)
|
||||
->whereNotNull('proof_image')
|
||||
->count();
|
||||
|
||||
return response()->json(['error' => false, 'data' => ['count' => $count]]);
|
||||
}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$query = Transaction::with('customer')
|
||||
->where('type', 'manual')
|
||||
->orderByDesc('created_at');
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$query->where('status', $request->integer('status'));
|
||||
}
|
||||
|
||||
if ($request->filled('filter')) {
|
||||
$search = $request->string('filter')->trim();
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('public_code', 'like', "%{$search}%")
|
||||
->orWhereHas('customer', fn($c) => $c
|
||||
->where('fullname', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%"));
|
||||
});
|
||||
}
|
||||
|
||||
$perPage = $request->integer('per_page', 15);
|
||||
$paginated = $query->paginate($perPage);
|
||||
|
||||
$items = $paginated->map(fn(Transaction $t) => [
|
||||
'id' => $t->id,
|
||||
'uuid' => $t->uuid,
|
||||
'public_code' => $t->public_code,
|
||||
'trans' => $t->trans,
|
||||
'amount' => $t->amount,
|
||||
'type' => $t->type,
|
||||
'status' => $t->status,
|
||||
'status_label' => TransactionStatus::from((int) $t->status)->label(),
|
||||
'proof_image' => $t->proof_image ? Storage::disk('public')->url($t->proof_image) : null,
|
||||
'created_at' => $t->created_at?->toISOString(),
|
||||
'customer' => $t->customer ? [
|
||||
'uuid' => $t->customer->uuid,
|
||||
'fullname' => $t->customer->fullname,
|
||||
'email' => $t->customer->email,
|
||||
'phone' => $t->customer->phone,
|
||||
'total' => $t->customer->total,
|
||||
'total_pending' => $t->customer->total_pending,
|
||||
] : null,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'data' => [
|
||||
'items' => $items,
|
||||
'total' => $paginated->total(),
|
||||
'per_page' => $paginated->perPage(),
|
||||
'current_page' => $paginated->currentPage(),
|
||||
'last_page' => $paginated->lastPage(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function approve(string $uuid): JsonResponse
|
||||
{
|
||||
$transaction = Transaction::with('customer')
|
||||
->where('uuid', $uuid)
|
||||
->where('type', 'manual')
|
||||
->where('status', TransactionStatus::PENDING->value)
|
||||
->first();
|
||||
|
||||
if (!$transaction) {
|
||||
return response()->json([
|
||||
'error' => true,
|
||||
'message' => 'Giao dịch không tồn tại hoặc không thể duyệt',
|
||||
], 404);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$transaction->update(['status' => TransactionStatus::COMPLETED->value]);
|
||||
|
||||
$customer = $transaction->customer;
|
||||
$customer->total += $transaction->amount;
|
||||
$customer->credit += (int) floor($transaction->amount / 100);
|
||||
$customer->total_pending = max(0, $customer->total_pending - $transaction->amount);
|
||||
$customer->save();
|
||||
|
||||
Log::create([
|
||||
'agent' => 'admin',
|
||||
'client_ip' => request()->ip(),
|
||||
'description' => 'Admin duyệt nạp tiền thủ công: ' . number_format($transaction->amount) . ' VNĐ (+' . floor($transaction->amount / 100) . ' credit)',
|
||||
'customer_id' => $customer->id,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
FacadesLog::error('AdminTransactionController::approve error: ' . $e->getMessage());
|
||||
return response()->json([
|
||||
'error' => true,
|
||||
'message' => 'Có lỗi xảy ra, vui lòng thử lại',
|
||||
], 500);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'message' => 'Đã duyệt giao dịch thành công',
|
||||
]);
|
||||
}
|
||||
|
||||
public function reject(string $uuid): JsonResponse
|
||||
{
|
||||
$transaction = Transaction::with('customer')
|
||||
->where('uuid', $uuid)
|
||||
->where('type', 'manual')
|
||||
->where('status', TransactionStatus::PENDING->value)
|
||||
->first();
|
||||
|
||||
if (!$transaction) {
|
||||
return response()->json([
|
||||
'error' => true,
|
||||
'message' => 'Giao dịch không tồn tại hoặc không thể từ chối',
|
||||
], 404);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$transaction->update(['status' => TransactionStatus::FAILED->value]);
|
||||
|
||||
$customer = $transaction->customer;
|
||||
$customer->total_pending = max(0, $customer->total_pending - $transaction->amount);
|
||||
$customer->save();
|
||||
|
||||
Log::create([
|
||||
'agent' => 'admin',
|
||||
'client_ip' => request()->ip(),
|
||||
'description' => 'Admin từ chối nạp tiền thủ công: ' . number_format($transaction->amount) . ' VNĐ',
|
||||
'customer_id' => $customer->id,
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
FacadesLog::error('AdminTransactionController::reject error: ' . $e->getMessage());
|
||||
return response()->json([
|
||||
'error' => true,
|
||||
'message' => 'Có lỗi xảy ra, vui lòng thử lại',
|
||||
], 500);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'message' => 'Đã từ chối giao dịch',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user