firstOrderDiscount.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { defineStore } from 'pinia'
  2. import { skillApi } from '~/api/skill'
  3. import type { FirstOrderDiscountChanceVo } from '~/types/api'
  4. /**
  5. * Store for first-order discount (首单1折) chance.
  6. * Bound to the current user; refresh on app start after login and when order status changes.
  7. */
  8. export const useFirstOrderDiscountStore = defineStore('firstOrderDiscount', {
  9. state: (): FirstOrderDiscountChanceVo => ({
  10. hasChance: false,
  11. discountRate: 0,
  12. eligiblePrice: 0,
  13. }),
  14. getters: {
  15. /**
  16. * Format discount rate for display: e.g. 0.1 (1折) => "90%" (90% off).
  17. */
  18. discountRatePercent: (state) => {
  19. const rate = state.discountRate
  20. if (rate <= 0 || rate >= 1)
  21. return '0%'
  22. return `${Math.round((1 - rate) * 100)}%`
  23. },
  24. /**
  25. * Get display price for a skill: apply discount when has chance, skill is eligible, and not own.
  26. * @param price - Original price (coins)
  27. * @param ownerUserNo - Skill owner userNo (optional)
  28. * @param currentUserNo - Current user's userNo; when equal to ownerUserNo, discount is not applied
  29. */
  30. getDisplayPrice: state => (price: number, ownerUserNo?: string, currentUserNo?: string) => {
  31. if (!state.hasChance || state.discountRate <= 0)
  32. return price
  33. if (ownerUserNo && currentUserNo && ownerUserNo === currentUserNo)
  34. return price
  35. if (state.eligiblePrice > 0 && price !== state.eligiblePrice)
  36. return price
  37. return Math.round(price * state.discountRate)
  38. },
  39. /** Whether discount is applied for this skill (for showing original + discounted) */
  40. isDiscountEligible: state => (price: number, ownerUserNo?: string, currentUserNo?: string) => {
  41. if (!state.hasChance || state.discountRate <= 0)
  42. return false
  43. if (ownerUserNo && currentUserNo && ownerUserNo === currentUserNo)
  44. return false
  45. if (state.eligiblePrice > 0 && price !== state.eligiblePrice)
  46. return false
  47. return true
  48. },
  49. },
  50. actions: {
  51. setChance(data: FirstOrderDiscountChanceVo) {
  52. this.hasChance = data.hasChance
  53. this.discountRate = data.discountRate
  54. this.eligiblePrice = data.eligiblePrice
  55. },
  56. clear() {
  57. this.hasChance = false
  58. this.discountRate = 0
  59. this.eligiblePrice = 0
  60. },
  61. /** Fetch current user's first-order discount chance. Call when logged in. */
  62. async fetchChance() {
  63. try {
  64. const data = await skillApi.firstOrderDiscountChance()
  65. this.setChance(data)
  66. return data
  67. }
  68. catch {
  69. this.clear()
  70. return null
  71. }
  72. },
  73. },
  74. })