GiftCards.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. namespace Longyi\Gift\Models;
  3. use Illuminate\Support\Str;
  4. use Longyi\Gift\Models\GiftCardUsageLog;
  5. use Illuminate\Database\Eloquent\Factories\HasFactory;
  6. use Illuminate\Database\Eloquent\Model;
  7. use Longyi\Gift\Contracts\GiftCards as GiftCardsContract;
  8. use Longyi\RewardPoints\Config\TransactionType;
  9. use Longyi\RewardPoints\Repositories\RewardPointRepository;
  10. use function Symfony\Component\Translation\t;
  11. use Illuminate\Support\Facades\DB;
  12. use Carbon\Carbon;
  13. class GiftCards extends Model implements GiftCardsContract
  14. {
  15. use HasFactory;
  16. protected $table = 'gift_cards';
  17. protected $fillable = [
  18. 'giftcard_number',
  19. 'giftcard_amount',
  20. 'used_giftcard_amount',
  21. 'remaining_giftcard_amount',
  22. 'customer_id',
  23. 'channel',
  24. 'expirationdate',
  25. 'giftcard_status',
  26. ];
  27. protected $casts = [
  28. 'giftcard_amount' => 'decimal:2',
  29. 'used_giftcard_amount' => 'decimal:2',
  30. 'remaining_giftcard_amount' => 'decimal:2',
  31. 'customer_id' => 'integer',
  32. 'expirationdate' => 'datetime',
  33. 'channel' => 'string',
  34. ];
  35. protected static function boot()
  36. {
  37. parent::boot();
  38. static::created(function ($giftCard) {
  39. GiftCardUsageLog::create([
  40. 'giftcard_number' => $giftCard->giftcard_number,
  41. 'customer_id' => $giftCard->customer_id,
  42. 'balance_before' => $giftCard->giftcard_amount,
  43. 'balance_after' => $giftCard->giftcard_amount,
  44. 'action_type' => GiftCardUsageLog::ACTION_ADD,
  45. 'status' => 'completed',
  46. 'notes' => $giftCard->channel ?: '',
  47. ]);
  48. });
  49. }
  50. public static function setGiftCardCode($giftcard)
  51. {
  52. if (!$giftcard) {
  53. return false;
  54. }
  55. $cart = cart()->getCart();
  56. $cart->giftcard_number = $giftcard->giftcard_number;
  57. $cart->save();
  58. return true;
  59. }
  60. public static function removeGiftCardCode()
  61. {
  62. $cart = cart()->getCart();
  63. $giftcardAmount = $cart->giftcard_amount ?? 0;
  64. // 清除购物车中的礼品卡标记
  65. $cart->giftcard_number = null;
  66. $cart->giftcard_amount = null;
  67. // 恢复购物车总额
  68. $cart->grand_total += $giftcardAmount;
  69. $cart->base_grand_total += $giftcardAmount;
  70. $cart->save();
  71. return true;
  72. }
  73. /**
  74. * Add a new gift card for a customer.
  75. *
  76. * @param int $customerId The ID of the customer to receive the gift card.
  77. * @param float $giftcardAmount The initial amount/balance of the gift card.
  78. * @param int $num Maximum number of gift cards allowed for this channel (0 means unlimited).
  79. * @param int $expirationdate Number of days until the gift card expires (default: 30 days).
  80. * @param string $channel The source channel or campaign identifier (default: 'activity_26_04_21').
  81. * @param string $notes Additional notes or description for the gift card creation.
  82. * @return void
  83. * @throws \Exception If customerId is empty, giftcardAmount is empty, or gift card limit exceeded.
  84. */
  85. public static function addGiftCard($customerId, $giftcardAmount, $num = 0, $expirationdate = 30, $channel = 'activity_26_04_21', $notes = '拉新活动添加')
  86. {
  87. if (empty($customerId)) {
  88. throw new \Exception('customerId is empty');
  89. }
  90. if (empty($giftcardAmount)) {
  91. throw new \Exception('giftcardAmount is empty');
  92. }
  93. $data = GiftCards::where('customer_id', $customerId)
  94. ->where('channel', $channel)
  95. ->count();
  96. if ($num != 0) {
  97. $giftNum =count($data);
  98. if($giftNum <= $num){
  99. throw new \Exception('gift num is not enough');
  100. }
  101. }
  102. $expirationDate = self::calculateExpirationDate($expirationdate);
  103. static::create([
  104. 'giftcard_number' => self::generateGiftCardNumber(),
  105. 'giftcard_amount' => $giftcardAmount,
  106. 'used_giftcard_amount' => 0,
  107. 'remaining_giftcard_amount' => $giftcardAmount,
  108. 'customer_id' => $customerId,
  109. 'expirationdate' => $expirationDate,
  110. 'giftcard_status' => 1,
  111. 'channel' => $channel,
  112. 'notes' => $notes
  113. ]);
  114. }
  115. /**
  116. * Calculate expiration date from various input types.
  117. *
  118. * @param mixed $expirationdate Days (int), date string, or Carbon instance
  119. * @return \Carbon\Carbon Calculated expiration date
  120. * @throws \Exception If the input type is invalid
  121. */
  122. protected static function calculateExpirationDate($expirationdate): Carbon
  123. {
  124. if ($expirationdate instanceof Carbon) {
  125. return $expirationdate;
  126. }
  127. if (is_string($expirationdate)) {
  128. try {
  129. return Carbon::parse($expirationdate);
  130. } catch (\Exception $e) {
  131. throw new \Exception("Invalid date format: {$expirationdate}. Use Y-m-d format.");
  132. }
  133. }
  134. if (is_int($expirationdate) || is_float($expirationdate)) {
  135. if ($expirationdate <= 0) {
  136. throw new \Exception('Expiration days must be greater than 0');
  137. }
  138. return now()->addDays((int) $expirationdate);
  139. }
  140. throw new \Exception('Invalid expiration date type. Must be integer (days), string (Y-m-d), or Carbon instance.');
  141. }
  142. public static function generateGiftCardNumber()
  143. {
  144. do {
  145. // Generate a random string of characters
  146. $randomString = Str::upper(Str::random(16));
  147. // Format the string into groups of four separated by dashes
  148. $code = implode('-', str_split($randomString, 4));
  149. // Check if the code already exists in the database
  150. $exists = GiftCards::where('giftcard_number', $code)->exists();
  151. } while ($exists);
  152. return $code;
  153. }
  154. public function giftcards()
  155. {
  156. return [
  157. 1 => [
  158. 'id' => 1,
  159. 'name' => 'Gift Card 1',
  160. 'amount' => 20.00,
  161. 'points' => 20,
  162. 'num' => 0,
  163. 'expirationdate' => 30,
  164. 'channel' => 'activity_26_04_21',
  165. 'notes' => ''
  166. ],
  167. 2 => [
  168. 'id' => 2,
  169. 'name' => 'Gift Card 2',
  170. 'amount' => 30.00,
  171. 'points' => 30,
  172. 'num' => 0,
  173. 'expirationdate' => 30,
  174. 'channel' => 'activity_26_04_21',
  175. 'notes' => ''
  176. ],
  177. 3 => [
  178. 'id' => 3,
  179. 'name' => 'Gift Card 3',
  180. 'amount' => 40.00,
  181. 'points' => 40,
  182. 'num' => 0,
  183. 'expirationdate' => 30,
  184. 'channel' => 'activity_26_04_21',
  185. 'notes' => ''
  186. ],
  187. 4 => [
  188. 'id' => 4,
  189. 'name' => 'Gift Card 4',
  190. 'amount' => 50.00,
  191. 'points' => 50,
  192. 'num' => 0,
  193. 'expirationdate' => 30,
  194. 'channel' => 'activity_26_04_21',
  195. 'notes' => ''
  196. ],
  197. ];
  198. }
  199. /**
  200. * Add a gift card for a customer using points
  201. *
  202. * @param int $id Gift card ID from giftcards() array
  203. * @param int $customerId Customer ID
  204. * @return array|true
  205. */
  206. public function add($id, $customerId)
  207. {
  208. if (empty($customerId)) {
  209. throw new \Exception('customer id is empty');
  210. }
  211. if (empty($id)) {
  212. throw new \Exception('gift card id is empty');
  213. }
  214. $gifts = $this->giftcards();
  215. $giftInfo = $gifts[$id] ?? null;
  216. if (!$giftInfo) {
  217. throw new \Exception("gift card not found (ID: {$id})");
  218. }
  219. $points = $giftInfo['points'] ?? 0;
  220. if ($points <= 0) {
  221. throw new \Exception("gift card points configuration error (points: {$points})");
  222. }
  223. try {
  224. DB::beginTransaction();
  225. self::addGiftCard(
  226. $customerId,
  227. $giftInfo['amount'],
  228. $giftInfo['num'],
  229. $giftInfo['expirationdate'],
  230. $giftInfo['channel'],
  231. $giftInfo['notes']
  232. );
  233. $rewardPointRepository = app(RewardPointRepository::class);
  234. $rewardPointRepository->deductPoints(
  235. $customerId,
  236. $points,
  237. TransactionType::GIFT_CARD_REDEEM,
  238. 'Gift Card Redeem'
  239. );
  240. DB::commit();
  241. return true;
  242. } catch (\Exception $e) {
  243. DB::rollBack();
  244. throw $e;
  245. }
  246. }
  247. }