ok(Plan::query()->with('entitlements')->orderByDesc('sort')->get()->map(fn (Plan $plan) => Presenter::plan($plan))); } public function storePlan(Request $request) { $plan = DB::transaction(function () use ($request) { $plan = Plan::query()->create($this->planData($request)); $this->syncEntitlements($plan, $request); return $plan->load('entitlements'); }); $this->audit($request->user(), 'plan.create', $plan, '新建方案 '.$plan->name); return $this->ok(Presenter::plan($plan)); } public function updatePlan(Request $request, Plan $plan) { DB::transaction(function () use ($request, $plan) { $plan->update($this->planData($request, $plan->id)); $this->syncEntitlements($plan, $request); }); $this->audit($request->user(), 'plan.update', $plan, '更新方案 '.$plan->name); return $this->ok(Presenter::plan($plan->load('entitlements'))); } public function orders(Request $request) { $query = Order::query()->with('user:id,nickname')->latest(); if ($status = $request->query('status')) { $query->where('status', $status); } return $this->ok($query->limit(100)->get()->map(fn (Order $order) => [ 'id' => $order->id, 'order_no' => $order->order_no, 'title' => $order->title, 'type' => $order->type, 'status' => $order->status, 'amount_cents' => $order->amount_cents, 'user' => $order->user?->nickname, 'remark' => $order->remark, 'created_at' => $order->created_at?->toIso8601String(), ])); } public function confirmOrder(Request $request, Order $order) { $data = $request->validate(['remark' => ['nullable', 'string', 'max:255']]); if (isset($data['remark'])) { $order->remark = $data['remark']; $order->save(); } $this->membership->confirmOrder($order->fresh()); $this->audit($request->user(), 'order.confirm', $order, '确认收款 '.$order->order_no); return $this->ok(null, '已确认收款'); } public function closeOrder(Request $request, Order $order) { if ($order->status !== 'pending') { return response()->json(['message' => '只能关闭待支付订单', 'code' => 'invalid_status'], 422); } $order->update(['status' => 'closed']); $this->audit($request->user(), 'order.close', $order, '关闭订单 '.$order->order_no); return $this->ok(null, '订单已关闭'); } public function tickets() { return $this->ok(ServiceTicket::query()->with('user:id,nickname', 'tool:id,name')->latest()->limit(100)->get()); } public function updateTicket(Request $request, ServiceTicket $ticket) { $data = $request->validate([ 'status' => ['required', 'in:pending,quoted,doing,checking,done,closed'], 'quote_cents' => ['nullable', 'integer', 'min:0'], 'delivery_note' => ['nullable', 'string'], ]); $ticket->update($data); $this->audit($request->user(), 'ticket.update', $ticket, '更新工单 #'.$ticket->id); return $this->ok($ticket); } public function deployments() { return $this->ok(Deployment::query()->with('user:id,nickname', 'tool:id,name')->latest()->limit(100)->get()); } public function storeDeployment(Request $request) { $data = $this->deploymentData($request); $deployment = Deployment::query()->create($data); $this->audit($request->user(), 'deployment.create', $deployment, '新建云端实例 #'.$deployment->id); return $this->ok($deployment); } public function updateDeployment(Request $request, Deployment $deployment) { $deployment->update($this->deploymentData($request)); $this->audit($request->user(), 'deployment.update', $deployment, '更新云端实例 #'.$deployment->id); return $this->ok($deployment); } private function planData(Request $request, ?int $id = null): array { return $request->validate([ 'code' => ['required', 'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/', Rule::unique('plans', 'code')->ignore($id)], 'name' => ['required', 'string', 'max:80'], 'price_cents' => ['required', 'integer', 'min:0'], 'period' => ['required', 'in:month,year,lifetime'], 'description' => ['nullable', 'string', 'max:255'], 'sort' => ['nullable', 'integer'], 'status' => ['required', 'in:published,hidden'], ]); } private function syncEntitlements(Plan $plan, Request $request): void { $items = $request->validate([ 'entitlements' => ['array'], 'entitlements.*.key' => ['required', 'string', 'max:120'], 'entitlements.*.value' => ['required', 'string', 'max:120'], ])['entitlements'] ?? []; $plan->entitlements()->delete(); foreach ($items as $item) { PlanEntitlement::query()->create([ 'plan_id' => $plan->id, 'entitlement_key' => $item['key'], 'entitlement_value' => $item['value'], ]); } } private function deploymentData(Request $request): array { return $request->validate([ 'user_id' => ['required', 'integer', 'exists:users,id'], 'tool_id' => ['nullable', 'integer', 'exists:tools,id'], 'spec' => ['nullable', 'string', 'max:120'], 'access_url' => ['nullable', 'string', 'max:500'], 'status' => ['required', 'in:pending,running,paused,released'], 'ends_at' => ['nullable', 'date'], 'remark' => ['nullable', 'string', 'max:255'], ]); } }