BackflowService.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <?php
  2. namespace App\Services;
  3. use App\Models\Backflow;
  4. use App\Support\BackflowPeriod;
  5. use Carbon\Carbon;
  6. use Exception;
  7. class BackflowService extends BaseService
  8. {
  9. public static string $MODEL = Backflow::class;
  10. public static function findOne(array $search): ?Backflow
  11. {
  12. return self::$MODEL::where(static::getWhere($search))->first();
  13. }
  14. /**
  15. * 进行回水操作
  16. * @param $id
  17. * @return bool
  18. * @throws Exception
  19. */
  20. public static function grant($id): bool
  21. {
  22. return RewardGrantService::grant('backflow', (int) $id, (string) (request()->user->username ?? 'system'));
  23. }
  24. /**
  25. * 记录本期充值/提现供列表展示,回水金额按分类亏损和规则计算。
  26. * @param $memberId string 会员ID
  27. * @param $changeAmount float 充值或提现金额
  28. * @return Backflow
  29. */
  30. public static function updateOrCreate(string $memberId, float $changeAmount): Backflow
  31. {
  32. $date = BackflowPeriod::forDate(Carbon::now(BackflowPeriod::TIMEZONE)->toDateString(), BackflowPeriod::dailyFrom());
  33. $field = $changeAmount > 0 ? 'recharge_amount' : 'withdrawal_amount';
  34. $query = static::$MODEL::query();
  35. if (strlen($date) === 10) $query->where('daily_date', $date);
  36. $backflow = $query->firstOrCreate(['date' => $date, 'member_id' => $memberId]);
  37. static::$MODEL::whereKey($backflow->id)->whereIn('status', Backflow::OPEN_STATUSES)->increment($field, $changeAmount);
  38. return $backflow->refresh();
  39. }
  40. /** 已发放 / 待发放 / 是否可发放 */
  41. public static function grantFields(array $row, ?string $dailyFrom = null, bool $hasUnsettled = false): array
  42. {
  43. $amount = bcadd((string) ($row['amount'] ?? 0), '0', 2);
  44. $granted = bcadd((string) ($row['granted_amount'] ?? 0), '0', 2);
  45. $open = in_array((int) ($row['status'] ?? 0), Backflow::OPEN_STATUSES, true);
  46. $pending = $open && bccomp($amount, $granted, 2) > 0 ? bcsub($amount, $granted, 2) : '0.00';
  47. $period = (string) ($row['date'] ?? '');
  48. $today = Carbon::now(BackflowPeriod::TIMEZONE)->toDateString();
  49. [, $end] = $period !== '' ? BackflowPeriod::bounds($period, $dailyFrom) : ['', $today];
  50. $closed = $period !== '' && $today >= $end;
  51. $waiting = $closed && $hasUnsettled && BackflowPeriod::waitsForSettlement($period, $dailyFrom);
  52. return [
  53. 'amount' => $amount,
  54. 'granted_amount' => $granted,
  55. 'pending_amount' => $pending,
  56. 'can_grant' => $open && !$waiting && ($closed || bccomp($pending, '0', 2) > 0),
  57. 'status_text' => [Backflow::STATUS_PENDING => '未发放', Backflow::STATUS_GRANTED => '已发放', Backflow::STATUS_PARTIAL => '部分发放'][(int) ($row['status'] ?? 0)] ?? '',
  58. ];
  59. }
  60. public static function getWhere(array $search = []): array
  61. {
  62. $where = [];
  63. if (isset($search['id']) && !empty($search['id'])) {
  64. $where[] = ['backflow.id', '=', $search['id']];
  65. }
  66. if (isset($search['member_id']) && !empty($search['member_id'])) {
  67. $where[] = ['backflow.member_id', '=', $search['member_id']];
  68. }
  69. if (isset($search['date']) && !empty($search['date'])) {
  70. $period = $search['date'];
  71. $where[] = strlen($period) === 7 ? ['backflow.date', 'like', $period . '%'] : ['backflow.date', '=', $period];
  72. }
  73. if (isset($search['status']) && $search['status'] != '') {
  74. $where[] = ['backflow.status', '=', $search['status']];
  75. }
  76. if (!empty($search['account'])) {
  77. $where[] = ['users.account', 'like', '%' . trim((string)$search['account']) . '%'];
  78. }
  79. if (!empty($search['first_name'])) {
  80. $where[] = ['users.first_name','like', '%'.$search['first_name'].'%'];
  81. }
  82. return $where;
  83. }
  84. public static function paginate(array $search = []): array
  85. {
  86. $today = Carbon::now(BackflowPeriod::TIMEZONE)->toDateString();
  87. $dailyFrom = BackflowPeriod::dailyFrom();
  88. $query = static::$MODEL::join('users', 'users.member_id', '=', 'backflow.member_id')
  89. ->where(static::getWhere($search))
  90. ->where('backflow.date', '<=', $today);
  91. \App\Support\MemberAudience::visibleUsers($query, 'users'); // 后台模拟会员只在模拟管理里出现
  92. $paginator = $query->select("backflow.*", "users.first_name", "users.account", "users.admin_note as user_admin_note")->orderByDesc('date')->orderBy('status')
  93. ->paginate($search['limit']);
  94. // 未结清的记录按最新注单和当前规则实时重算(规则修改后存储值可能过期),与点“发放”时的计算一致
  95. $tiers = $labels = null;
  96. foreach ($paginator->items() as $item) {
  97. $hasUnsettled = false;
  98. if (in_array((int) $item->status, Backflow::OPEN_STATUSES, true)) {
  99. $tiers ??= RewardRuleService::tiers('backflow');
  100. $labels ??= RewardRuleService::categories('backflow');
  101. $user = RewardBasisService::user((string) $item->member_id);
  102. $basis = $user ? RewardBasisService::backflowBasis($user, (string) $item->date, $dailyFrom) : [];
  103. $live = RewardBasisService::backflowReward($basis, $tiers, $labels);
  104. $hasUnsettled = $live['has_unsettled'];
  105. $item['amount'] = $live['amount'];
  106. $item['loss_amount'] = $live['loss_amount'];
  107. $item['need_flow'] = $live['need_flow'];
  108. $item['details'] = $live['details'];
  109. }
  110. foreach (self::grantFields($item->toArray(), $dailyFrom, $hasUnsettled) as $field => $value) $item[$field] = $value;
  111. }
  112. // $count = $query->count();
  113. // $query->with(['user'])->orderByDesc('date')->orderBy('status');
  114. // $list = $query->forPage($search['page'], $search['limit'])->get()->toArray();
  115. // return ['total' => $count, 'data' => $list];
  116. return ['total' => $paginator->total(), 'data' => $paginator->items()];
  117. }
  118. }