report(fn ($params) => $service->memberDays($params), $this->dateListRules()); } public function promoterDetails(ShareEarningReportService $service) { return $this->report(fn ($params) => $service->promoterDetails($params), $this->detailRules()); } public function registrationDetails(ShareEarningReportService $service) { return $this->report(fn ($params) => $service->registrationDetails($params), $this->detailRules()); } public function summary(ShareEarningReportService $service) { return $this->report(fn ($params) => $service->summary($params), $this->dateListRules([ 'account' => ['nullable', 'string', 'max:100'], ])); } public function settings() { return $this->run(fn () => ShareSetting::query()->first() ?: [ 'id' => null, 'enabled' => 0, 'share_domain' => '', 'valid_member_min_deposit' => '20.00', 'benefit_enabled' => 0, 'non_seller_threshold' => '200.00', 'tier_one_rate' => '4.0000', 'tier_two_rate' => '6.0000', ]); } public function saveSettings() { return $this->run(function () { $params = request()->validate([ 'enabled' => ['required', Rule::in([0, 1])], 'share_domain' => ['required', 'string', 'max:500'], 'valid_member_min_deposit' => ['required', 'numeric', 'gte:0'], 'benefit_enabled' => ['required', Rule::in([0, 1])], 'non_seller_threshold' => ['required', 'numeric', 'gte:0'], 'tier_one_rate' => ['required', 'numeric', 'between:0,100'], 'tier_two_rate' => ['required', 'numeric', 'between:0,100'], ]); $domain = trim($params['share_domain']); if (!preg_match('~^https?://~i', $domain)) $domain = 'https://' . $domain; if (!filter_var($domain, FILTER_VALIDATE_URL)) { throw ValidationException::withMessages(['share_domain' => '分享赚钱域名格式错误']); } $params['share_domain'] = rtrim($domain, '/'); return DB::transaction(function () use ($params) { $settingId = ShareSetting::query()->value('id'); $setting = $settingId ? ShareSetting::query()->lockForUpdate()->findOrFail($settingId) : ShareSetting::query()->create([]); $before = $setting->toArray(); $setting->fill($params + OperationAuditService::actor())->save(); OperationAuditService::record('share_setting', (int)$setting->id, 'update', $before, $setting); return $setting->fresh(); }); }); } public function blacklists() { return $this->run(function () { $params = request()->validate([ 'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:200'], 'account' => ['nullable', 'string', 'max:100'], ]); $query = ShareBlacklist::query(); if (!empty($params['account'])) { $ids = User::query()->where('account', 'like', '%' . $params['account'] . '%')->pluck('id'); $query->whereIn('user_id', $ids); } $total = $query->count(); $rows = $query->orderByDesc('id')->forPage((int)($params['page'] ?? 1), (int)($params['limit'] ?? 20))->get(); $users = User::query()->whereIn('id', $rows->pluck('user_id'))->get()->keyBy('id'); $rows->each(function ($row) use ($users) { $user = $users->get($row->user_id); $row->account = (string)($user->account ?? ''); $row->nickname = (string)($user->first_name ?? ''); }); return ['total' => $total, 'data' => $rows]; }); } public function saveBlacklist() { return $this->run(function () { $params = request()->validate([ 'id' => ['nullable', 'integer'], 'account' => ['required_without:id', 'string', 'max:100'], 'blocked_categories' => ['required', 'array', 'min:1'], 'blocked_categories.*' => [Rule::in(['sports', 'lottery', 'third_party_game'])], 'status' => ['nullable', Rule::in([0, 1])], ]); return DB::transaction(function () use ($params) { $id = (int)($params['id'] ?? 0); $model = $id ? ShareBlacklist::query()->lockForUpdate()->findOrFail($id) : new ShareBlacklist(); $before = $model->exists ? $model->toArray() : []; if (!$id) { $user = User::query()->where('account', $params['account'])->first(); if (!$user) throw new \RuntimeException('会员账号不存在'); $existing = ShareBlacklist::withTrashed()->where('user_id', $user->id)->lockForUpdate()->first(); if ($existing && !$existing->trashed()) throw new \RuntimeException('该会员已在禁止推广列表'); if ($existing) { $model = $existing; $model->restore(); $before = []; } else { $model->user_id = $user->id; $model->member_id = (string)$user->member_id; } } $model->blocked_categories = array_values(array_unique($params['blocked_categories'])); $model->status = (int)($params['status'] ?? ($model->status ?? 1)); $model->fill(OperationAuditService::actor())->save(); OperationAuditService::record('share_blacklist', (int)$model->id, $id ? 'update' : 'create', $before, $model); return $model->fresh(); }); }); } public function blacklistStatus() { return $this->run(function () { $params = request()->validate(['id' => ['required', 'integer'], 'status' => ['required', Rule::in([0, 1])]]); return DB::transaction(function () use ($params) { $model = ShareBlacklist::query()->lockForUpdate()->findOrFail($params['id']); $before = $model->toArray(); $model->fill(['status' => $params['status']] + OperationAuditService::actor())->save(); OperationAuditService::record('share_blacklist', (int)$model->id, 'status', $before, $model); return $model->fresh(); }); }); } public function deleteBlacklist() { return $this->run(function () { $params = request()->validate(['id' => ['required', 'integer']]); return DB::transaction(function () use ($params) { $model = ShareBlacklist::query()->lockForUpdate()->findOrFail($params['id']); $before = $model->toArray(); $model->delete(); OperationAuditService::record('share_blacklist', (int)$model->id, 'delete', $before, []); return []; }); }); } private function dateListRules(array $extra = []): array { return [ 'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:200'], 'start_date' => ['nullable', 'date_format:Y-m-d'], 'end_date' => ['nullable', 'date_format:Y-m-d', 'after_or_equal:start_date'], ] + $extra; } private function detailRules(): array { return [ 'date' => ['required', 'date_format:Y-m-d'], 'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:200'], 'member_id' => ['nullable', 'string', 'max:64'], 'account' => ['nullable', 'string', 'max:100'], ]; } private function report(callable $callback, array $rules) { return $this->run(fn () => $callback(request()->validate($rules))); } private function run(callable $callback) { try { return $this->success($callback()); } catch (ValidationException $e) { return $this->error(HttpStatus::CUSTOM_ERROR, $e->validator->errors()->first()); } catch (Throwable $e) { return $this->error(HttpStatus::CUSTOM_ERROR, $e->getMessage()); } } }