ShareEarning.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. <?php
  2. namespace App\Http\Controllers\admin;
  3. use App\Constants\HttpStatus;
  4. use App\Http\Controllers\Controller;
  5. use App\Models\ShareBlacklist;
  6. use App\Models\ShareSetting;
  7. use App\Models\User;
  8. use App\Services\OperationAuditService;
  9. use App\Services\ShareEarningReportService;
  10. use Illuminate\Support\Facades\DB;
  11. use Illuminate\Validation\Rule;
  12. use Illuminate\Validation\ValidationException;
  13. use Throwable;
  14. class ShareEarning extends Controller
  15. {
  16. public function memberList(ShareEarningReportService $service)
  17. {
  18. return $this->report(fn ($params) => $service->memberDays($params), $this->dateListRules());
  19. }
  20. public function promoterDetails(ShareEarningReportService $service)
  21. {
  22. return $this->report(fn ($params) => $service->promoterDetails($params), $this->detailRules());
  23. }
  24. public function registrationDetails(ShareEarningReportService $service)
  25. {
  26. return $this->report(fn ($params) => $service->registrationDetails($params), $this->detailRules());
  27. }
  28. public function summary(ShareEarningReportService $service)
  29. {
  30. return $this->report(fn ($params) => $service->summary($params), $this->dateListRules([
  31. 'account' => ['nullable', 'string', 'max:100'],
  32. ]));
  33. }
  34. public function settings()
  35. {
  36. return $this->run(fn () => ShareSetting::query()->first() ?: [
  37. 'id' => null,
  38. 'enabled' => 0,
  39. 'share_domain' => '',
  40. 'valid_member_min_deposit' => '20.00',
  41. 'benefit_enabled' => 0,
  42. 'non_seller_threshold' => '200.00',
  43. 'tier_one_rate' => '4.0000',
  44. 'tier_two_rate' => '6.0000',
  45. ]);
  46. }
  47. public function saveSettings()
  48. {
  49. return $this->run(function () {
  50. $params = request()->validate([
  51. 'enabled' => ['required', Rule::in([0, 1])],
  52. 'share_domain' => ['required', 'string', 'max:500'],
  53. 'valid_member_min_deposit' => ['required', 'numeric', 'gte:0'],
  54. 'benefit_enabled' => ['required', Rule::in([0, 1])],
  55. 'non_seller_threshold' => ['required', 'numeric', 'gte:0'],
  56. 'tier_one_rate' => ['required', 'numeric', 'between:0,100'],
  57. 'tier_two_rate' => ['required', 'numeric', 'between:0,100'],
  58. ]);
  59. $domain = trim($params['share_domain']);
  60. if (!preg_match('~^https?://~i', $domain)) $domain = 'https://' . $domain;
  61. if (!filter_var($domain, FILTER_VALIDATE_URL)) {
  62. throw ValidationException::withMessages(['share_domain' => '分享赚钱域名格式错误']);
  63. }
  64. $params['share_domain'] = rtrim($domain, '/');
  65. return DB::transaction(function () use ($params) {
  66. $settingId = ShareSetting::query()->value('id');
  67. $setting = $settingId
  68. ? ShareSetting::query()->lockForUpdate()->findOrFail($settingId)
  69. : ShareSetting::query()->create([]);
  70. $before = $setting->toArray();
  71. $setting->fill($params + OperationAuditService::actor())->save();
  72. OperationAuditService::record('share_setting', (int)$setting->id, 'update', $before, $setting);
  73. return $setting->fresh();
  74. });
  75. });
  76. }
  77. public function blacklists()
  78. {
  79. return $this->run(function () {
  80. $params = request()->validate([
  81. 'page' => ['nullable', 'integer', 'min:1'],
  82. 'limit' => ['nullable', 'integer', 'min:1', 'max:200'],
  83. 'account' => ['nullable', 'string', 'max:100'],
  84. ]);
  85. $query = ShareBlacklist::query();
  86. if (!empty($params['account'])) {
  87. $ids = User::query()->where('account', 'like', '%' . $params['account'] . '%')->pluck('id');
  88. $query->whereIn('user_id', $ids);
  89. }
  90. $total = $query->count();
  91. $rows = $query->orderByDesc('id')->forPage((int)($params['page'] ?? 1), (int)($params['limit'] ?? 20))->get();
  92. $users = User::query()->whereIn('id', $rows->pluck('user_id'))->get()->keyBy('id');
  93. $rows->each(function ($row) use ($users) {
  94. $user = $users->get($row->user_id);
  95. $row->account = (string)($user->account ?? '');
  96. $row->nickname = (string)($user->first_name ?? '');
  97. });
  98. return ['total' => $total, 'data' => $rows];
  99. });
  100. }
  101. public function saveBlacklist()
  102. {
  103. return $this->run(function () {
  104. $params = request()->validate([
  105. 'id' => ['nullable', 'integer'],
  106. 'account' => ['required_without:id', 'string', 'max:100'],
  107. 'blocked_categories' => ['required', 'array', 'min:1'],
  108. 'blocked_categories.*' => [Rule::in(['sports', 'lottery', 'third_party_game'])],
  109. 'status' => ['nullable', Rule::in([0, 1])],
  110. ]);
  111. return DB::transaction(function () use ($params) {
  112. $id = (int)($params['id'] ?? 0);
  113. $model = $id ? ShareBlacklist::query()->lockForUpdate()->findOrFail($id) : new ShareBlacklist();
  114. $before = $model->exists ? $model->toArray() : [];
  115. if (!$id) {
  116. $user = User::query()->where('account', $params['account'])->first();
  117. if (!$user) throw new \RuntimeException('会员账号不存在');
  118. $existing = ShareBlacklist::withTrashed()->where('user_id', $user->id)->lockForUpdate()->first();
  119. if ($existing && !$existing->trashed()) throw new \RuntimeException('该会员已在禁止推广列表');
  120. if ($existing) {
  121. $model = $existing;
  122. $model->restore();
  123. $before = [];
  124. } else {
  125. $model->user_id = $user->id;
  126. $model->member_id = (string)$user->member_id;
  127. }
  128. }
  129. $model->blocked_categories = array_values(array_unique($params['blocked_categories']));
  130. $model->status = (int)($params['status'] ?? ($model->status ?? 1));
  131. $model->fill(OperationAuditService::actor())->save();
  132. OperationAuditService::record('share_blacklist', (int)$model->id, $id ? 'update' : 'create', $before, $model);
  133. return $model->fresh();
  134. });
  135. });
  136. }
  137. public function blacklistStatus()
  138. {
  139. return $this->run(function () {
  140. $params = request()->validate(['id' => ['required', 'integer'], 'status' => ['required', Rule::in([0, 1])]]);
  141. return DB::transaction(function () use ($params) {
  142. $model = ShareBlacklist::query()->lockForUpdate()->findOrFail($params['id']);
  143. $before = $model->toArray();
  144. $model->fill(['status' => $params['status']] + OperationAuditService::actor())->save();
  145. OperationAuditService::record('share_blacklist', (int)$model->id, 'status', $before, $model);
  146. return $model->fresh();
  147. });
  148. });
  149. }
  150. public function deleteBlacklist()
  151. {
  152. return $this->run(function () {
  153. $params = request()->validate(['id' => ['required', 'integer']]);
  154. return DB::transaction(function () use ($params) {
  155. $model = ShareBlacklist::query()->lockForUpdate()->findOrFail($params['id']);
  156. $before = $model->toArray();
  157. $model->delete();
  158. OperationAuditService::record('share_blacklist', (int)$model->id, 'delete', $before, []);
  159. return [];
  160. });
  161. });
  162. }
  163. private function dateListRules(array $extra = []): array
  164. {
  165. return [
  166. 'page' => ['nullable', 'integer', 'min:1'],
  167. 'limit' => ['nullable', 'integer', 'min:1', 'max:200'],
  168. 'start_date' => ['nullable', 'date_format:Y-m-d'],
  169. 'end_date' => ['nullable', 'date_format:Y-m-d', 'after_or_equal:start_date'],
  170. ] + $extra;
  171. }
  172. private function detailRules(): array
  173. {
  174. return [
  175. 'date' => ['required', 'date_format:Y-m-d'],
  176. 'page' => ['nullable', 'integer', 'min:1'],
  177. 'limit' => ['nullable', 'integer', 'min:1', 'max:200'],
  178. 'member_id' => ['nullable', 'string', 'max:64'],
  179. 'account' => ['nullable', 'string', 'max:100'],
  180. ];
  181. }
  182. private function report(callable $callback, array $rules)
  183. {
  184. return $this->run(fn () => $callback(request()->validate($rules)));
  185. }
  186. private function run(callable $callback)
  187. {
  188. try {
  189. return $this->success($callback());
  190. } catch (ValidationException $e) {
  191. return $this->error(HttpStatus::CUSTOM_ERROR, $e->validator->errors()->first());
  192. } catch (Throwable $e) {
  193. return $this->error(HttpStatus::CUSTOM_ERROR, $e->getMessage());
  194. }
  195. }
  196. }