RewardPointRepository.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <?php
  2. namespace Longyi\RewardPoints\Repositories;
  3. use Webkul\Core\Eloquent\Repository;
  4. use Longyi\RewardPoints\Models\RewardPointHistory;
  5. use Longyi\RewardPoints\Models\RewardPointCustomer;
  6. use Longyi\RewardPoints\Models\RewardActiveRule;
  7. use Carbon\Carbon;
  8. use Illuminate\Support\Facades\DB;
  9. use Illuminate\Support\Facades\Log;
  10. class RewardPointRepository extends Repository
  11. {
  12. public function model()
  13. {
  14. return RewardPointHistory::class;
  15. }
  16. public function getCustomerPoints($customerId)
  17. {
  18. $customerPoints = RewardPointCustomer::where('customer_id', $customerId)->first();
  19. if (!$customerPoints) {
  20. return 0;
  21. }
  22. return $customerPoints->mw_reward_point;
  23. }
  24. /**
  25. * 添加积分(支持传入规则信息)
  26. */
  27. public function addPoints($customerId, $type, $amount, $orderId = null, $detail = null, $rule = null)
  28. {
  29. try {
  30. return DB::transaction(function () use ($customerId, $type, $amount, $orderId, $detail, $rule) {
  31. // 1. 获取或创建用户积分记录
  32. $customerPoints = RewardPointCustomer::firstOrCreate(
  33. ['customer_id' => $customerId],
  34. [
  35. 'mw_reward_point' => 0,
  36. 'mw_friend_id' => 0,
  37. 'subscribed_balance_update' => 1,
  38. 'subscribed_point_expiration' => 1,
  39. 'last_checkout' => Carbon::now()
  40. ]
  41. );
  42. // 2. 使用 increment 方法增加积分
  43. $customerPoints->increment('mw_reward_point', $amount);
  44. // 3. 更新最后检查时间
  45. $customerPoints->last_checkout = Carbon::now();
  46. $customerPoints->save();
  47. // 4. 重新查询获取最新的余额
  48. $customerPoints = RewardPointCustomer::where('customer_id', $customerId)->first();
  49. $newBalance = $customerPoints ? (int) $customerPoints->mw_reward_point : $amount;
  50. Log::info('Points added', [
  51. 'customer_id' => $customerId,
  52. 'type' => $type,
  53. 'amount' => $amount,
  54. 'new_balance' => $newBalance
  55. ]);
  56. // 5. 获取过期信息(如果没有传入规则,则查询)
  57. $expiredDay = 0;
  58. $expiredTime = null;
  59. if ($rule) {
  60. $expiredDay = $rule->expired_day ?? 0;
  61. $expiredTime = $expiredDay > 0 ? Carbon::now()->addDays($expiredDay) : null;
  62. } else {
  63. // 兼容旧代码,如果没有传入规则则查询
  64. $rule = RewardActiveRule::where('type_of_transaction', $type)
  65. ->where('status', 1)
  66. ->first();
  67. $expiredDay = $rule ? $rule->expired_day : 0;
  68. $expiredTime = $expiredDay > 0 ? Carbon::now()->addDays($expiredDay) : null;
  69. }
  70. // 6. 创建历史记录
  71. $history = $this->create([
  72. 'customer_id' => $customerId,
  73. 'type_of_transaction' => $type,
  74. 'amount' => $amount,
  75. 'balance' => $newBalance,
  76. 'transaction_detail' => $detail,
  77. 'transaction_time' => Carbon::now(),
  78. 'history_order_id' => $orderId ?? 0,
  79. 'expired_day' => $expiredDay,
  80. 'expired_time' => $expiredTime,
  81. 'point_remaining' => $amount,
  82. 'check_time' => 1,
  83. 'status' => RewardPointHistory::STATUS_COMPLETED
  84. ]);
  85. return $history;
  86. });
  87. } catch (\Exception $e) {
  88. Log::error('Error adding points', [
  89. 'customer_id' => $customerId,
  90. 'error' => $e->getMessage(),
  91. 'trace' => $e->getTraceAsString()
  92. ]);
  93. throw $e;
  94. }
  95. }
  96. /**
  97. * 批量添加积分(用于同时多个事件)
  98. * 确保每条历史记录都有正确的余额值
  99. */
  100. public function addPointsBatch($customerId, array $pointsList)
  101. {
  102. try {
  103. return DB::transaction(function () use ($customerId, $pointsList) {
  104. // 1. 获取或创建用户积分记录(不使用 lockForUpdate 避免问题)
  105. $customerPoints = RewardPointCustomer::firstOrCreate(
  106. ['customer_id' => $customerId],
  107. [
  108. 'mw_reward_point' => 0,
  109. 'mw_friend_id' => 0,
  110. 'subscribed_balance_update' => 1,
  111. 'subscribed_point_expiration' => 1,
  112. 'last_checkout' => Carbon::now()
  113. ]
  114. );
  115. // 确保 $customerPoints 存在
  116. if (!$customerPoints) {
  117. throw new \Exception('Failed to create or retrieve customer points record');
  118. }
  119. // 2. 获取当前余额
  120. $currentBalance = (int) $customerPoints->mw_reward_point;
  121. $histories = [];
  122. $totalAmount = 0;
  123. // 3. 逐条创建历史记录(每条记录使用累加后的余额)
  124. foreach ($pointsList as $index => $pointData) {
  125. $amount = (int) $pointData['amount'];
  126. $type = $pointData['type'];
  127. $detail = $pointData['detail'] ?? null;
  128. $orderId = $pointData['order_id'] ?? null;
  129. $rule = $pointData['rule'] ?? null;
  130. // 累加总积分
  131. $totalAmount += $amount;
  132. // 计算当前这条记录后的余额(累加后的余额)
  133. $newBalance = $currentBalance + $totalAmount;
  134. // 获取过期信息
  135. $expiredDay = 0;
  136. $expiredTime = null;
  137. if ($rule) {
  138. $expiredDay = $rule->expired_day ?? 0;
  139. $expiredTime = $expiredDay > 0 ? Carbon::now()->addDays($expiredDay) : null;
  140. }
  141. // 创建历史记录(每条记录独立)
  142. $history = $this->create([
  143. 'customer_id' => $customerId,
  144. 'type_of_transaction' => $type,
  145. 'amount' => $amount,
  146. 'balance' => $newBalance,
  147. 'transaction_detail' => $detail,
  148. 'transaction_time' => Carbon::now(),
  149. 'history_order_id' => $orderId ?? 0,
  150. 'expired_day' => $expiredDay,
  151. 'expired_time' => $expiredTime,
  152. 'point_remaining' => $amount,
  153. 'check_time' => 1,
  154. 'status' => RewardPointHistory::STATUS_COMPLETED
  155. ]);
  156. $histories[] = $history;
  157. }
  158. // 4. 最后一次性更新用户总积分
  159. $updated = DB::table('mw_reward_point_customer')
  160. ->where('customer_id', $customerId)
  161. ->update([
  162. 'mw_reward_point' => DB::raw('mw_reward_point + ' . (float)$totalAmount),
  163. 'last_checkout' => Carbon::now()
  164. ]);
  165. return $histories;
  166. });
  167. } catch (\Exception $e) {
  168. Log::error('Error adding batch points', [
  169. 'customer_id' => $customerId,
  170. 'error' => $e->getMessage(),
  171. 'trace' => $e->getTraceAsString()
  172. ]);
  173. throw $e;
  174. }
  175. }
  176. public function deductPoints($customerId, $amount, $orderId = null, $detail = null)
  177. {
  178. try {
  179. return DB::transaction(function () use ($customerId, $amount, $orderId, $detail) {
  180. $customerPoints = RewardPointCustomer::where('customer_id', $customerId)
  181. ->lockForUpdate()
  182. ->first();
  183. if (!$customerPoints) {
  184. Log::warning('Customer points record not found', ['customer_id' => $customerId]);
  185. return false;
  186. }
  187. if ($customerPoints->mw_reward_point < $amount) {
  188. Log::warning('Insufficient points', [
  189. 'customer_id' => $customerId,
  190. 'current' => $customerPoints->mw_reward_point,
  191. 'required' => $amount
  192. ]);
  193. return false;
  194. }
  195. $currentBalance = (int) $customerPoints->mw_reward_point;
  196. $newBalance = $currentBalance - (int) $amount;
  197. // 更新积分
  198. $customerPoints->mw_reward_point = $newBalance;
  199. $customerPoints->last_checkout = Carbon::now();
  200. $customerPoints->save();
  201. // 创建历史记录
  202. $history = $this->create([
  203. 'customer_id' => $customerId,
  204. 'type_of_transaction' => 0,
  205. 'amount' => -$amount,
  206. 'balance' => $newBalance,
  207. 'transaction_detail' => $detail,
  208. 'transaction_time' => Carbon::now(),
  209. 'history_order_id' => $orderId ?? 0,
  210. 'expired_day' => 0,
  211. 'expired_time' => null,
  212. 'point_remaining' => 0,
  213. 'check_time' => 1,
  214. 'status' => RewardPointHistory::STATUS_COMPLETED
  215. ]);
  216. Log::info('Points deducted successfully', [
  217. 'customer_id' => $customerId,
  218. 'amount' => $amount,
  219. 'new_balance' => $newBalance
  220. ]);
  221. return $history;
  222. });
  223. } catch (\Exception $e) {
  224. Log::error('Error deducting points', [
  225. 'customer_id' => $customerId,
  226. 'amount' => $amount,
  227. 'error' => $e->getMessage()
  228. ]);
  229. return false;
  230. }
  231. }
  232. public function getHistory($customerId, $limit = 20)
  233. {
  234. return $this->where('customer_id', $customerId)
  235. ->orderBy('transaction_time', 'desc')
  236. ->paginate($limit);
  237. }
  238. public function checkExpiredPoints()
  239. {
  240. $expiredHistories = $this->where('expired_time', '<', Carbon::now())
  241. ->where('status', RewardPointHistory::STATUS_COMPLETED)
  242. ->where('point_remaining', '>', 0)
  243. ->get();
  244. foreach ($expiredHistories as $history) {
  245. try {
  246. DB::transaction(function () use ($history) {
  247. $customerPoints = RewardPointCustomer::where('customer_id', $history->customer_id)
  248. ->lockForUpdate()
  249. ->first();
  250. if ($customerPoints && $customerPoints->mw_reward_point >= $history->point_remaining) {
  251. $customerPoints->mw_reward_point -= $history->point_remaining;
  252. $customerPoints->save();
  253. $history->status = RewardPointHistory::STATUS_EXPIRED;
  254. $history->point_remaining = 0;
  255. $history->save();
  256. Log::info('Expired points processed', [
  257. 'history_id' => $history->history_id,
  258. 'customer_id' => $history->customer_id,
  259. 'points' => $history->point_remaining
  260. ]);
  261. }
  262. });
  263. } catch (\Exception $e) {
  264. Log::error('Error processing expired points', [
  265. 'history_id' => $history->history_id,
  266. 'error' => $e->getMessage()
  267. ]);
  268. }
  269. }
  270. }
  271. }