GiftRegistry.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. namespace Longyi\FreeGift\Services;
  3. use Longyi\FreeGift\Config\Gift;
  4. use Longyi\FreeGift\Repositories\FreeGiftItemRepository;
  5. use Webkul\Checkout\Contracts\Cart as CartContract;
  6. /**
  7. * 单次请求内的赠品注册表(按购物车维度)。
  8. *
  9. * 对应 Amasty 的 Model\Registry,但这里额外负责:
  10. * - 首次访问时从 free_gift_items 回填状态(reservedQty / is_item_deleted)
  11. * - 规则重算结束后把结果写回 free_gift_items
  12. */
  13. class GiftRegistry
  14. {
  15. /**
  16. * @var array<int, GiftItemsGroup>
  17. */
  18. protected array $groups = [];
  19. /**
  20. * @var array<int, bool>
  21. */
  22. protected array $hydrated = [];
  23. public function __construct(
  24. protected FreeGiftItemRepository $giftItemRepository
  25. ) {}
  26. /**
  27. * 取(或初始化)某个购物车的赠品分组。
  28. */
  29. public function group(int $cartId): GiftItemsGroup
  30. {
  31. if (! isset($this->groups[$cartId])) {
  32. $this->groups[$cartId] = new GiftItemsGroup();
  33. }
  34. if (! isset($this->hydrated[$cartId])) {
  35. $this->hydrate($cartId);
  36. $this->hydrated[$cartId] = true;
  37. }
  38. return $this->groups[$cartId];
  39. }
  40. /**
  41. * 用购物车里的真实赠品行刷新 reservedQty。
  42. *
  43. * free_gift_items 只是缓存,cart_items 才是真相;每次重算都以购物车为准,
  44. * 避免用户在另一个标签页删了东西导致数量对不上。
  45. */
  46. public function syncReservedQty(CartContract $cart, GiftItemsGroup $group): void
  47. {
  48. $reserved = [];
  49. foreach ($cart->all_items as $cartItem) {
  50. $ruleId = $cartItem->additional[Gift::CART_ITEM_FLAG] ?? null;
  51. if (! $ruleId) {
  52. continue;
  53. }
  54. $key = ((int) $ruleId).':'.$cartItem->sku;
  55. $reserved[$key] = ($reserved[$key] ?? 0) + (float) $cartItem->quantity;
  56. }
  57. foreach ($group->getItems() as $item) {
  58. $item->setReservedQty($reserved[$item->getKey()] ?? 0);
  59. unset($reserved[$item->getKey()]);
  60. }
  61. // 购物车里存在但注册表里没有的赠品行(规则被删掉等)交给 syncGiftItems 清理
  62. }
  63. /**
  64. * 把注册表写回 free_gift_items。
  65. */
  66. public function persist(int $cartId): void
  67. {
  68. if (! isset($this->groups[$cartId])) {
  69. return;
  70. }
  71. foreach ($this->groups[$cartId]->getItems() as $item) {
  72. // 规则已经不再命中,且购物车里也没有残留 → 直接删掉状态行
  73. if ($item->getAllowedQty() < 1) {
  74. if ($item->getId()) {
  75. $this->giftItemRepository->delete($item->getId());
  76. $item->setId(null);
  77. }
  78. continue;
  79. }
  80. $data = [
  81. 'cart_id' => $cartId,
  82. 'cart_item_id' => $item->getCartItemId(),
  83. 'product_id' => $item->getProductId(),
  84. 'sku' => $item->getSku(),
  85. 'rule_id' => $item->getRuleId(),
  86. 'rule_type' => $item->getRuleType(),
  87. 'allowed_qty' => $item->getAllowedQty(),
  88. 'reserved_qty' => $item->getReservedQty(),
  89. 'discount_amount' => $item->getDiscount(),
  90. 'auto_add' => $item->isAutoAdd(),
  91. 'is_item_deleted' => $item->isDeleted(),
  92. ];
  93. if ($item->getId()) {
  94. $this->giftItemRepository->update($data, $item->getId());
  95. } else {
  96. $row = $this->giftItemRepository->create($data);
  97. $item->setId((int) $row->id);
  98. }
  99. }
  100. }
  101. /**
  102. * 丢弃某个购物车的注册表缓存(购物车被清空 / 合并时使用)。
  103. */
  104. public function forget(int $cartId): void
  105. {
  106. unset($this->groups[$cartId], $this->hydrated[$cartId]);
  107. }
  108. /**
  109. * 从 free_gift_items 回填。
  110. */
  111. protected function hydrate(int $cartId): void
  112. {
  113. foreach ($this->giftItemRepository->getByCart($cartId) as $row) {
  114. $item = new GiftItem(
  115. (string) $row->sku,
  116. (int) $row->rule_id,
  117. $row->discount_amount,
  118. (int) $row->rule_type,
  119. (bool) $row->auto_add
  120. );
  121. $item->setId((int) $row->id)
  122. ->setProductId($row->product_id ? (int) $row->product_id : null)
  123. ->setCartItemId($row->cart_item_id ? (int) $row->cart_item_id : null)
  124. ->setReservedQty((float) $row->reserved_qty)
  125. ->setStoredQty((float) $row->allowed_qty)
  126. ->setDeleted((bool) $row->is_item_deleted);
  127. $this->groups[$cartId]->put($item);
  128. }
  129. }
  130. }