diff --git a/app/Http/Controllers/Admin/IeoController.php b/app/Http/Controllers/Admin/IeoController.php new file mode 100644 index 0000000..5165d5d --- /dev/null +++ b/app/Http/Controllers/Admin/IeoController.php @@ -0,0 +1,190 @@ +paginate($request->input('limit', 20)); + return $this->layuiData($data); + } + + public function projectSave(Request $request) + { + $id = $request->input('id'); + $fields = $request->only([ + 'name','symbol','description','whitepaper_url','thumb_image', + 'token_price','total_supply','min_buy','max_buy','start_time','end_time', + 'type','status','fake_progress', + 'subscription_limit','announce_time','listing_time','unlock_time' + ]); + // Normalize empty datetime strings to null to avoid MySQL errors + foreach (['announce_time','listing_time','unlock_time','start_time','end_time'] as $k) { + if (isset($fields[$k]) && $fields[$k] === '') $fields[$k] = null; + } + if ($id) { + IeoProject::where('id', $id)->update($fields); + } else { + IeoProject::create($fields); + } + return $this->success('OK'); + } + + public function projectDelete(Request $request) + { + IeoProject::destroy($request->input('id')); + return $this->success('Deleted'); + } + + public function orderList(Request $request) + { + $query = IeoOrder::query() + ->leftJoin('users', 'ieo_order.user_id', '=', 'users.id') + ->leftJoin('ieo_project', 'ieo_order.project_id', '=', 'ieo_project.id') + ->select('ieo_order.*', 'users.phone as user_phone', 'users.email as user_email', 'ieo_project.name as project_name', 'ieo_project.symbol'); + + $project_id = $request->input('project_id'); + if ($project_id) $query->where('ieo_order.project_id', $project_id); + + $data = $query->orderBy('ieo_order.id', 'desc')->paginate($request->input('limit', 20)); + $statusMap = ['0'=>'待开奖','1'=>'已中签','2'=>'未中签']; + $forceMap = [null=>'-','1'=>'强制中签','2'=>'强制未中']; + foreach ($data as $item) { + $item->status_text = $statusMap[$item->status] ?? ''; + $item->force_text = $forceMap[$item->force_result] ?? '-'; + } + return $this->layuiData($data); + } + + public function forceResult(Request $request) + { + $ids = $request->input('ids'); + $result = $request->input('result'); // 1=win, 2=lose + if (!$ids) return $this->error('No orders selected'); + IeoOrder::whereIn('id', explode(',', $ids))->update(['force_result' => $result]); + return $this->success('Set ' . ($result == 1 ? 'Force Win' : 'Force Lose')); + } + + public function lottery(Request $request) + { + $project_id = $request->input('project_id'); + $project = IeoProject::find($project_id); + if (!$project) return $this->error('Project not found'); + + $orders = IeoOrder::where('project_id', $project_id)->where('status', 0)->get(); + if ($orders->isEmpty()) return $this->error('No pending orders'); + + $won = 0; $lost = 0; + DB::beginTransaction(); + try { + foreach ($orders as $order) { + if ($order->force_result == 1) $result = 1; + elseif ($order->force_result == 2) $result = 2; + else $result = rand(1, 100) <= 50 ? 1 : 2; + + if ($result == 1) { + $order->update(['status' => 1, 'token_amount' => $project->token_price > 0 ? sprintf("%.6f", $order->amount / $project->token_price) : 0]); + $won++; + } else { + $wallet = UsersWallet::where('user_id', $order->user_id)->where('currency', 3)->first(); + if ($wallet) { + $before = $wallet->legal_balance; + $wallet->legal_balance = bc_add($wallet->legal_balance, $order->amount, 6); + $wallet->save(); + AccountLog::insertLog( + ['user_id'=>$order->user_id,'value'=>$order->amount,'info'=>'IEO Refund - '.$project->name,'type'=>AccountLog::IEO_OPERATION,'currency'=>3], + ['balance_type'=>1,'wallet_id'=>$wallet->id,'lock_type'=>0,'before'=>$before,'change'=>$order->amount,'after'=>$wallet->legal_balance] + ); + } + $order->update(['status' => 2]); + $lost++; + } + } + $creditOrders = \App\CreditIeoOrder::where('project_id', $project_id)->where('status', 0)->get(); + $creditWon = 0; $creditLost = 0; + foreach ($creditOrders as $co) { + $cResult = rand(1, 100) <= 50 ? 1 : 2; + if ($cResult == 1) { + $co->update([ + 'status' => 4, + 'token_amount' => $project->token_price > 0 ? sprintf("%.6f", $co->amount / $project->token_price) : 0, + ]); + $creditWon++; + } else { + $line = \App\CreditLine::find($co->credit_line_id); + if ($line) { + $line->used_amount = max(0, bc_sub($line->used_amount, $co->amount, 6)); + $line->available_amount = bc_add($line->available_amount, $co->amount, 6); + if ($line->available_amount > $line->total_amount) $line->available_amount = $line->total_amount; + $line->save(); + } + $co->update(['status' => 2]); + $creditLost++; + } + } + + $project->update(['status' => 2]); + DB::commit(); + return $this->success("Lottery: {$won} won/{$lost} lost, Credit: {$creditWon} won/{$creditLost} lost"); + } catch (\Exception $e) { + DB::rollBack(); + return $this->error($e->getMessage()); + } + } + + public function generateCodes(Request $request) + { + $project_id = intval($request->input('project_id')); + $count = intval($request->input('count')); + $amount = floatval($request->input('amount')); + if (!$project_id || $count <= 0 || $amount <= 0) return $this->error('Invalid params'); + if ($count > 1000) return $this->error('Max 1000 per batch'); + + $project = IeoProject::find($project_id); + if (!$project) return $this->error('Project not found'); + + $generated = 0; $attempts = 0; + $maxAttempts = $count * 5; + while ($generated < $count && $attempts < $maxAttempts) { + $attempts++; + $code = strtoupper(substr(str_shuffle('ABCDEFGHJKLMNPQRSTUVWXYZ23456789'), 0, 6)); + $exists = IeoAllocationCode::where('code', $code)->exists(); + if ($exists) continue; + IeoAllocationCode::create([ + 'project_id' => $project_id, + 'code' => $code, + 'amount' => $amount, + 'used' => 0, + 'user_id' => 0, + ]); + $generated++; + } + return $this->success("Generated {$generated} codes"); + } + + public function codeList(Request $request) + { + $project_id = $request->input('project_id'); + $query = IeoAllocationCode::query() + ->leftJoin('users', 'ieo_allocation_code.user_id', '=', 'users.id') + ->leftJoin('ieo_project', 'ieo_allocation_code.project_id', '=', 'ieo_project.id') + ->select('ieo_allocation_code.*', 'users.phone as user_phone', 'users.email as user_email', 'ieo_project.name as project_name'); + if ($project_id) $query->where('ieo_allocation_code.project_id', $project_id); + $data = $query->orderBy('ieo_allocation_code.id', 'desc')->paginate($request->input('limit', 50)); + foreach ($data as $item) { + $item->used_text = $item->used ? 'Used' : 'Available'; + } + return $this->layuiData($data); + } +} diff --git a/app/Http/Controllers/Api/IeoController.php b/app/Http/Controllers/Api/IeoController.php new file mode 100644 index 0000000..651ce1a --- /dev/null +++ b/app/Http/Controllers/Api/IeoController.php @@ -0,0 +1,183 @@ +input('status', 'all'); + $query = IeoProject::query(); + if ($status !== 'all') $query->where('status', $status); + $list = $query->orderBy('id', 'desc')->get(); + foreach ($list as $item) { + $real_pct = $item->total_supply > 0 ? round($item->real_sold / $item->total_supply * 100, 2) : 0; + $item->progress = $item->type == 1 ? max($item->fake_progress, $real_pct) : min($item->fake_progress, 100); + } + return $this->success($list); + } + + public function detail(Request $request) + { + $id = $request->input('id'); + $project = IeoProject::find($id); + if (!$project) return $this->error('Not found'); + $real_pct = $project->total_supply > 0 ? round($project->real_sold / $project->total_supply * 100, 2) : 0; + $project->progress = $project->type == 1 ? max($project->fake_progress, $real_pct) : min($project->fake_progress, 100); + + $user_id = Token::getUserIdByToken(Token::getToken()); + $user = \App\Users::find($user_id); + $project->my_order = null; + $project->my_order_count = 0; + if ($user) { + $order = IeoOrder::where('user_id', $user->id)->where('project_id', $id)->orderBy('id', 'desc')->first(); + if ($order) { + $statusMap = ['0' => 'Pending', '1' => 'Won', '2' => 'Lost']; + $order->status_text = $statusMap[$order->status] ?? ''; + $project->my_order = $order; + } + $project->my_order_count = IeoOrder::where('user_id', $user->id)->where('project_id', $id)->count(); + } + return $this->success($project); + } + + public function subscribe(Request $request) + { + $user_id = Token::getUserIdByToken(Token::getToken()); + $user = \App\Users::find($user_id); + if (!$user) return $this->error('Please login'); + $project_id = $request->input('project_id'); + $amount = floatval($request->input('amount')); + $pay_pwd = $request->input('pay_pwd'); + + if ($amount <= 0) return $this->error('Invalid amount'); + $project = IeoProject::find($project_id); + if (!$project || $project->status != 1) return $this->error('IEO not active'); + if ($amount < $project->min_buy) return $this->error('Min: ' . $project->min_buy . ' USDC'); + if ($project->max_buy > 0 && $amount > $project->max_buy) return $this->error('Max: ' . $project->max_buy . ' USDC'); + + $limit = intval($project->subscription_limit); + if ($limit > 0) { + $userOrderCount = IeoOrder::where('user_id', $user->id)->where('project_id', $project_id)->count(); + if ($userOrderCount >= $limit) return $this->error('Subscription limit reached: ' . $limit); + } + + $existingCredit = \App\CreditIeoOrder::where('user_id', $user->id)->where('project_id', $project_id)->whereIn('status', [0, 1])->exists(); + if ($existingCredit) return $this->error('Already subscribed via credit IEO'); + + $wallet = UsersWallet::where('user_id', $user->id)->where('currency', 3)->lockForUpdate()->first(); + if (!$wallet || $wallet->legal_balance < $amount) return $this->error('Insufficient balance'); + + DB::beginTransaction(); + try { + $before = $wallet->legal_balance; + $wallet->legal_balance = bc_sub($wallet->legal_balance, $amount, 6); + $wallet->save(); + AccountLog::insertLog( + ['user_id' => $user->id, 'value' => -$amount, 'info' => 'IEO Subscribe - ' . $project->name, 'type' => AccountLog::IEO_OPERATION, 'currency' => 3], + ['balance_type' => 1, 'wallet_id' => $wallet->id, 'lock_type' => 0, 'before' => $before, 'change' => -$amount, 'after' => $wallet->legal_balance] + ); + $token_amount = $project->token_price > 0 ? sprintf("%.6f", $amount / $project->token_price) : 0; + IeoOrder::create([ + 'user_id' => $user->id, 'project_id' => $project_id, + 'amount' => $amount, 'token_amount' => $token_amount, 'status' => 0, + ]); + $project->increment('real_sold', $token_amount); + DB::commit(); + return $this->success('Subscription successful'); + } catch (\Exception $e) { + DB::rollBack(); + return $this->error($e->getMessage()); + } + } + + public function myOrders(Request $request) + { + $user_id = Token::getUserIdByToken(Token::getToken()); + $user = \App\Users::find($user_id); + if (!$user) return $this->error('Please login'); + $list = IeoOrder::where('user_id', $user->id)->with('project:id,name,symbol')->orderBy('id', 'desc')->get(); + $statusMap = ['0' => 'Pending', '1' => 'Won', '2' => 'Lost']; + foreach ($list as $item) { + $item->status_text = $statusMap[$item->status] ?? ''; + } + return $this->success($list); + } + + public function redeemCode(Request $request) + { + $user_id = Token::getUserIdByToken(Token::getToken()); + $user = \App\Users::find($user_id); + if (!$user) return $this->error('Please login'); + $code = trim((string)$request->input('code')); + $project_id = intval($request->input('project_id')); + if ($code === '' || !$project_id) return $this->error('Invalid params'); + + $project = IeoProject::find($project_id); + if (!$project || $project->status != 1) return $this->error('IEO not active'); + + DB::beginTransaction(); + try { + $allocCode = IeoAllocationCode::where('project_id', $project_id) + ->where('code', $code) + ->lockForUpdate() + ->first(); + if (!$allocCode) { DB::rollBack(); return $this->error('Invalid code'); } + if ($allocCode->used) { DB::rollBack(); return $this->error('Code already used'); } + + $amount = floatval($allocCode->amount); + if ($amount <= 0) { DB::rollBack(); return $this->error('Invalid code amount'); } + + $wallet = UsersWallet::where('user_id', $user->id)->where('currency', 3)->first(); + if (!$wallet || $wallet->legal_balance < $amount) { DB::rollBack(); return $this->error('Insufficient balance'); } + + $before = $wallet->legal_balance; + $wallet->legal_balance = bc_sub($wallet->legal_balance, $amount, 6); + $wallet->save(); + AccountLog::insertLog( + ['user_id' => $user->id, 'value' => -$amount, 'info' => 'IEO Allocation - ' . $project->name, 'type' => AccountLog::IEO_OPERATION, 'currency' => 3], + ['balance_type' => 1, 'wallet_id' => $wallet->id, 'lock_type' => 0, 'before' => $before, 'change' => -$amount, 'after' => $wallet->legal_balance] + ); + + $token_amount = $project->token_price > 0 ? sprintf("%.6f", $amount / $project->token_price) : 0; + IeoOrder::create([ + 'user_id' => $user->id, 'project_id' => $project_id, + 'amount' => $amount, 'token_amount' => $token_amount, 'status' => 1, + ]); + $project->increment('real_sold', $token_amount); + + $allocCode->used = 1; + $allocCode->user_id = $user->id; + $allocCode->save(); + + DB::commit(); + return $this->success('Redeemed successfully'); + } catch (\Exception $e) { + DB::rollBack(); + return $this->error($e->getMessage()); + } + } + + public function allocationOrders(Request $request) + { + $user_id = Token::getUserIdByToken(Token::getToken()); + $user = \App\Users::find($user_id); + if (!$user) return $this->error('Please login'); + $list = IeoAllocationCode::where('user_id', $user->id) + ->where('used', 1) + ->with('project:id,name,symbol,token_price,listing_time,unlock_time') + ->orderBy('id', 'desc') + ->get(); + return $this->success($list); + } +} diff --git a/app/IeoAllocationCode.php b/app/IeoAllocationCode.php new file mode 100644 index 0000000..d0fcee6 --- /dev/null +++ b/app/IeoAllocationCode.php @@ -0,0 +1,12 @@ +belongsTo(IeoProject::class, 'project_id'); } + public function user() { return $this->belongsTo(Users::class, 'user_id'); } +} diff --git a/app/IeoOrder.php b/app/IeoOrder.php new file mode 100644 index 0000000..4d69ad0 --- /dev/null +++ b/app/IeoOrder.php @@ -0,0 +1,12 @@ +belongsTo(IeoProject::class, 'project_id'); } + public function user() { return $this->belongsTo(Users::class, 'user_id'); } +} diff --git a/app/IeoProject.php b/app/IeoProject.php new file mode 100644 index 0000000..e758389 --- /dev/null +++ b/app/IeoProject.php @@ -0,0 +1,9 @@ + +
IEO订单管理
+
+
+ + + + +
+
+
+ +@endsection +@section('scripts') + +@endsection diff --git a/resources/views/manages/ieo/projects.blade.php b/resources/views/manages/ieo/projects.blade.php new file mode 100644 index 0000000..50113b9 --- /dev/null +++ b/resources/views/manages/ieo/projects.blade.php @@ -0,0 +1,162 @@ +@extends('admin._layoutNew') +@section('title', 'IEO项目管理') +@section('page-content') +
+
IEO项目管理
+
+ + +
+
+
+ + + + + +@endsection +@section('scripts') + +@endsection