rewards.blade.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. @extends('base.base')
  2. @section('base')
  3. <div class="main-panel">
  4. <div class="content-wrapper">
  5. <div class="page-header">
  6. <h3 class="page-title">
  7. <span class="page-title-icon bg-gradient-primary text-white mr-2">
  8. <i class="mdi mdi-trophy"></i>
  9. </span>
  10. World Cup 奖励审核
  11. </h3>
  12. <nav aria-label="breadcrumb">
  13. <ol class="breadcrumb">
  14. <li class="breadcrumb-item"><a href="#">活动管理</a></li>
  15. <li class="breadcrumb-item active" aria-current="page">奖励审核</li>
  16. </ol>
  17. </nav>
  18. </div>
  19. <div class="row">
  20. <div class="col-md-3 stretch-card grid-margin">
  21. <div class="card bg-gradient-warning text-white">
  22. <div class="card-body">
  23. <h4 class="font-weight-normal mb-3">待审核</h4>
  24. <h2 class="mb-0">{{ $kpi['reviewing_count'] ?? 0 }}</h2>
  25. </div>
  26. </div>
  27. </div>
  28. <div class="col-md-3 stretch-card grid-margin">
  29. <div class="card bg-gradient-success text-white">
  30. <div class="card-body">
  31. <h4 class="font-weight-normal mb-3">已通过</h4>
  32. <h2 class="mb-0">{{ $kpi['approved_count'] ?? 0 }}</h2>
  33. </div>
  34. </div>
  35. </div>
  36. <div class="col-md-3 stretch-card grid-margin">
  37. <div class="card bg-gradient-danger text-white">
  38. <div class="card-body">
  39. <h4 class="font-weight-normal mb-3">已驳回</h4>
  40. <h2 class="mb-0">{{ $kpi['rejected_count'] ?? 0 }}</h2>
  41. </div>
  42. </div>
  43. </div>
  44. <div class="col-md-3 stretch-card grid-margin">
  45. <div class="card bg-gradient-info text-white">
  46. <div class="card-body">
  47. <h4 class="font-weight-normal mb-3">已赔付</h4>
  48. <h2 class="mb-0">${{ number_format(($kpi['paid_liability'] ?? 0) / 100, 2) }}</h2>
  49. </div>
  50. </div>
  51. </div>
  52. </div>
  53. <div class="row">
  54. <div class="col-lg-12 grid-margin stretch-card">
  55. <div class="card">
  56. <div class="card-body">
  57. <h4 class="card-title">审核队列</h4>
  58. <form class="form-inline mb-3" method="get" action="/admin/world-cup/rewards">
  59. <div class="form-group mr-2 mb-2">
  60. <label class="mr-2">状态</label>
  61. <select class="form-control" name="status">
  62. @foreach(['reviewing' => '待审核', 'approved' => '已通过', 'rejected' => '已驳回', 'on_hold' => '挂起', 'clawed_back' => '已追回', '' => '全部'] as $value => $label)
  63. <option value="{{ $value }}" @if(($filters['status'] ?? '') === $value) selected @endif>{{ $label }}</option>
  64. @endforeach
  65. </select>
  66. </div>
  67. <div class="form-group mr-2 mb-2">
  68. <label class="mr-2">风险</label>
  69. <select class="form-control" name="risk">
  70. @foreach(['' => '全部', 'low' => 'Low', 'medium' => 'Medium', 'high' => 'High'] as $value => $label)
  71. <option value="{{ $value }}" @if(($filters['risk'] ?? '') === $value) selected @endif>{{ $label }}</option>
  72. @endforeach
  73. </select>
  74. </div>
  75. <div class="form-group mr-2 mb-2">
  76. <label class="mr-2">搜索</label>
  77. <input class="form-control" name="q" value="{{ $filters['q'] ?? '' }}" placeholder="Reward/User ID">
  78. </div>
  79. <div class="form-group mr-2 mb-2">
  80. <label class="mr-2">条数</label>
  81. <input class="form-control" name="limit" value="{{ $filters['limit'] ?? 100 }}" style="width: 90px;">
  82. </div>
  83. <button type="submit" class="btn btn-gradient-primary btn-sm mb-2">查询</button>
  84. </form>
  85. <div class="mb-3">
  86. <button type="button" class="btn btn-success btn-sm js-batch" data-action="approve">批量通过</button>
  87. <button type="button" class="btn btn-danger btn-sm js-batch" data-action="reject">批量驳回</button>
  88. <span class="text-muted ml-2">High risk 奖励必须单笔审核,批量通过会被服务端拒绝。</span>
  89. </div>
  90. <div class="table-responsive">
  91. <table class="table table-bordered table-hover">
  92. <thead>
  93. <tr>
  94. <th><input type="checkbox" class="batch-all"></th>
  95. <th>Reward ID</th>
  96. <th>邀请人</th>
  97. <th>被邀请人</th>
  98. <th>首充</th>
  99. <th>各得奖励</th>
  100. <th>总赔付</th>
  101. <th>风险</th>
  102. <th>状态</th>
  103. <th>提交时间</th>
  104. <th>审核人</th>
  105. <th>操作</th>
  106. </tr>
  107. </thead>
  108. <tbody>
  109. @forelse($list as $row)
  110. <tr>
  111. <td><input type="checkbox" class="td-check reward-check" value="{{ $row['reward_id'] }}"></td>
  112. <td>{{ $row['reward_id'] }}</td>
  113. <td>{{ $row['referrer_id'] }}</td>
  114. <td>{{ $row['invitee_id'] }}</td>
  115. <td>${{ number_format(($row['first_deposit_amt'] ?? 0) / 100, 2) }}</td>
  116. <td>${{ number_format(($row['reward_each'] ?? 0) / 100, 2) }}</td>
  117. <td>${{ number_format(($row['total_liability'] ?? 0) / 100, 2) }}</td>
  118. <td>
  119. <span class="badge badge-{{ ($row['risk_level'] ?? '') === 'high' ? 'danger' : (($row['risk_level'] ?? '') === 'medium' ? 'warning' : 'success') }}">
  120. {{ $row['risk_level'] ?? '-' }} / {{ $row['risk_score'] ?? 0 }}
  121. </span>
  122. </td>
  123. <td>{{ $row['status'] ?? '-' }}</td>
  124. <td>{{ $row['submitted_at'] ?? '-' }}</td>
  125. <td>{{ $row['review_by'] ?? '-' }}</td>
  126. <td style="min-width: 240px;">
  127. <button type="button" class="btn btn-outline-info btn-sm js-detail" data-id="{{ $row['reward_id'] }}">详情</button>
  128. @if(($row['status'] ?? '') === 'reviewing')
  129. <button type="button" class="btn btn-success btn-sm js-action" data-action="approve" data-id="{{ $row['reward_id'] }}">通过</button>
  130. <button type="button" class="btn btn-danger btn-sm js-action" data-action="reject" data-id="{{ $row['reward_id'] }}">驳回</button>
  131. <button type="button" class="btn btn-warning btn-sm js-action" data-action="hold" data-id="{{ $row['reward_id'] }}">挂起</button>
  132. @elseif(($row['status'] ?? '') === 'approved')
  133. <button type="button" class="btn btn-dark btn-sm js-action" data-action="clawback" data-id="{{ $row['reward_id'] }}">追回</button>
  134. @endif
  135. </td>
  136. </tr>
  137. @empty
  138. <tr>
  139. <td colspan="12" class="text-center text-muted">暂无数据</td>
  140. </tr>
  141. @endforelse
  142. </tbody>
  143. </table>
  144. </div>
  145. </div>
  146. </div>
  147. </div>
  148. </div>
  149. <div class="row">
  150. <div class="col-lg-12 grid-margin stretch-card">
  151. <div class="card">
  152. <div class="card-body">
  153. <h4 class="card-title">最近审核日志</h4>
  154. <div class="table-responsive">
  155. <table class="table table-bordered">
  156. <thead>
  157. <tr>
  158. <th>ID</th>
  159. <th>Reward ID</th>
  160. <th>操作人</th>
  161. <th>动作</th>
  162. <th>原因</th>
  163. <th>状态变化</th>
  164. <th>时间</th>
  165. </tr>
  166. </thead>
  167. <tbody>
  168. @forelse($logs as $log)
  169. <tr>
  170. <td>{{ $log['id'] ?? '-' }}</td>
  171. <td>{{ $log['reward_id'] ?? '-' }}</td>
  172. <td>{{ $log['actor'] ?? '-' }}</td>
  173. <td>{{ $log['action'] ?? '-' }}</td>
  174. <td>{{ $log['reason_code'] ?? '-' }}</td>
  175. <td>{{ $log['before_status'] ?? '-' }} -> {{ $log['after_status'] ?? '-' }}</td>
  176. <td>{{ $log['created_at'] ?? '-' }}</td>
  177. </tr>
  178. @empty
  179. <tr>
  180. <td colspan="7" class="text-center text-muted">暂无日志</td>
  181. </tr>
  182. @endforelse
  183. </tbody>
  184. </table>
  185. </div>
  186. </div>
  187. </div>
  188. </div>
  189. </div>
  190. </div>
  191. </div>
  192. <script>
  193. (function () {
  194. function showMessage(message) {
  195. if (window.layer) {
  196. layer.msg(message);
  197. return;
  198. }
  199. alert(message);
  200. }
  201. function reloadWhenSuccess(res) {
  202. showMessage(res.msg || 'success');
  203. if (res.code === 200) {
  204. window.location.reload();
  205. }
  206. }
  207. function postReview(url, data) {
  208. myRequest(url, 'post', data || {}, reloadWhenSuccess);
  209. }
  210. function requireReason(title) {
  211. return window.prompt(title || '请输入原因代码,例如 same_device / bulk_signup / fraud');
  212. }
  213. $('.js-action').on('click', function () {
  214. var id = $(this).data('id');
  215. var action = $(this).data('action');
  216. var data = {};
  217. if (action === 'reject') {
  218. data.reason_code = requireReason('请输入驳回原因代码');
  219. if (!data.reason_code) {
  220. return;
  221. }
  222. }
  223. if (action === 'clawback') {
  224. data.reason_code = requireReason('请输入追回原因代码');
  225. if (!data.reason_code) {
  226. return;
  227. }
  228. data.ban_users = window.confirm('是否同时封禁邀请人与被邀请人?') ? 1 : 0;
  229. }
  230. if (action === 'approve' && !window.confirm('确认通过并立即给双方发放奖励?')) {
  231. return;
  232. }
  233. postReview('/admin/world-cup/rewards/' + id + '/' + action, data);
  234. });
  235. $('.js-batch').on('click', function () {
  236. var action = $(this).data('action');
  237. var ids = $('.reward-check:checked').map(function () {
  238. return $(this).val();
  239. }).get();
  240. var data = {
  241. action: action,
  242. reward_ids: ids
  243. };
  244. if (ids.length === 0) {
  245. showMessage('请先选择奖励单');
  246. return;
  247. }
  248. if (action === 'reject') {
  249. data.reason_code = requireReason('请输入批量驳回原因代码');
  250. if (!data.reason_code) {
  251. return;
  252. }
  253. }
  254. if (action === 'approve' && !window.confirm('确认批量通过?High risk 会被服务端拒绝。')) {
  255. return;
  256. }
  257. postReview('/admin/world-cup/rewards/batch', data);
  258. });
  259. $('.js-detail').on('click', function () {
  260. var id = $(this).data('id');
  261. myRequest('/admin/world-cup/rewards/' + id, 'get', {}, function (res) {
  262. if (res.code !== 200) {
  263. showMessage(res.msg || '查询失败');
  264. return;
  265. }
  266. var content = '<pre style="white-space: pre-wrap; padding: 12px;">'
  267. + $('<div/>').text(JSON.stringify(res.data.reward, null, 2)).html()
  268. + '</pre>';
  269. if (window.layer) {
  270. layer.open({
  271. type: 1,
  272. area: ['720px', '620px'],
  273. title: '奖励详情 #' + id,
  274. content: content
  275. });
  276. return;
  277. }
  278. alert(JSON.stringify(res.data.reward, null, 2));
  279. });
  280. });
  281. })();
  282. </script>
  283. @endsection