user.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { defineStore } from 'pinia'
  2. import { authApi, profileApi } from '../utils/api'
  3. /** Normalize vipEndTime from Jackson array [y,m,d,h,m,s] to ISO string */
  4. function normalizeDate(v) {
  5. if (!v) return null
  6. if (Array.isArray(v)) {
  7. const [y, m, d, h = 0, min = 0, s = 0] = v
  8. const pad = (n) => String(n).padStart(2, '0')
  9. return `${y}-${pad(m)}-${pad(d)}T${pad(h)}:${pad(min)}:${pad(s)}`
  10. }
  11. return v
  12. }
  13. export const useUserStore = defineStore('user', {
  14. state: () => ({
  15. token: uni.getStorageSync('token') || '',
  16. nickname: '',
  17. avatarUrl: '',
  18. isVip: false,
  19. vipType: null,
  20. vipEndTime: null,
  21. referralCode: '',
  22. // Daily quota
  23. usedCharts: 0,
  24. maxCharts: 3,
  25. usedChats: 0,
  26. maxChats: 3,
  27. usedAcademic: 0,
  28. maxAcademic: 1,
  29. shareCount: 0,
  30. shareLimit: 1,
  31. // Commission
  32. totalCommission: 0,
  33. availableCommission: 0,
  34. // US-5.1 Profile fields
  35. profileIncomplete: false,
  36. gender: 0,
  37. birthDate: null,
  38. bio: '',
  39. city: '',
  40. tags: '',
  41. lookingFor: 0,
  42. profileComplete: false,
  43. birthYear: 0,
  44. birthMonth: 0,
  45. birthDay: 0
  46. }),
  47. getters: {
  48. isLoggedIn: (state) => !!state.token,
  49. isVipActive: (state) => {
  50. if (!state.vipEndTime) return false
  51. return new Date(state.vipEndTime) > new Date()
  52. }
  53. },
  54. actions: {
  55. async login(openid, nickname, avatarUrl, referralCode) {
  56. const data = { openid, nickname, avatarUrl }
  57. if (referralCode) data.referralCode = referralCode
  58. const res = await authApi.login(data)
  59. const tokenStr = res.token || res
  60. this.token = tokenStr
  61. uni.setStorageSync('token', tokenStr)
  62. this.nickname = nickname
  63. this.avatarUrl = avatarUrl
  64. // US-5.1 §5-7: Check profile completion
  65. this.profileIncomplete = res.profileIncomplete === true
  66. },
  67. async wechatLogin(code, nickname, avatarUrl, referralCode) {
  68. const data = { code, nickname, avatarUrl }
  69. if (referralCode) data.referralCode = referralCode
  70. const res = await authApi.login(data)
  71. const tokenStr = res.token || res
  72. this.token = tokenStr
  73. uni.setStorageSync('token', tokenStr)
  74. this.nickname = nickname || '微信用户'
  75. this.avatarUrl = avatarUrl || ''
  76. // US-5.1 §5-7: Check profile completion
  77. this.profileIncomplete = res.profileIncomplete === true
  78. // Fetch full profile after login
  79. try { await this.fetchProfile() } catch (e) {}
  80. },
  81. async fetchProfile() {
  82. const user = await profileApi.info()
  83. this.nickname = user.nickname
  84. this.avatarUrl = user.avatarUrl
  85. this.referralCode = user.referralCode
  86. this.vipType = user.vipType || null
  87. // Normalize vipEndTime: Jackson may send [2026,6,8,23,59,59]; always store as ISO string
  88. this.vipEndTime = normalizeDate(user.vipEndTime)
  89. this.isVip = !!this.vipEndTime && new Date(this.vipEndTime) > new Date()
  90. // US-5.1 profile fields
  91. this.gender = user.gender || 0
  92. this.birthDate = user.birthDate || null
  93. this.bio = user.bio || ''
  94. this.city = user.city || ''
  95. this.tags = user.tags || ''
  96. this.lookingFor = user.lookingFor || 0
  97. this.profileComplete = !!user.profileComplete
  98. this.birthYear = user.birthYear || 0
  99. this.birthMonth = user.birthMonth || 0
  100. this.birthDay = user.birthDay || 0
  101. this.profileIncomplete = !this.profileComplete
  102. },
  103. async fetchQuota() {
  104. try {
  105. const quota = await profileApi.quota()
  106. this.usedCharts = quota.dailyChartCount || 0
  107. this.maxCharts = quota.chartLimit > 0 ? quota.chartLimit : 999
  108. this.usedChats = quota.dailyChatCount || 0
  109. this.maxChats = quota.chatLimit > 0 ? quota.chatLimit : 999
  110. this.shareCount = quota.dailyShareCount || 0
  111. this.shareLimit = quota.shareLimit > 0 ? quota.shareLimit : 1
  112. this.usedAcademic = quota.dailyAcademicCount || 0
  113. this.maxAcademic = quota.academicLimit > 0 ? quota.academicLimit : 1
  114. } catch (e) { /* ignore */ }
  115. },
  116. async fetchCommissionSummary() {
  117. try {
  118. const summary = await profileApi.commissionSummary()
  119. this.totalCommission = summary.totalEarnings || 0
  120. this.availableCommission = summary.availableBalance || 0
  121. } catch (e) { /* ignore */ }
  122. },
  123. logout() {
  124. this.token = ''
  125. this.nickname = ''
  126. this.avatarUrl = ''
  127. this.usedCharts = 0
  128. this.usedChats = 0
  129. this.usedAcademic = 0
  130. this.shareCount = 0
  131. this.shareLimit = 1
  132. this.maxAcademic = 1
  133. this.totalCommission = 0
  134. this.availableCommission = 0
  135. this.profileIncomplete = false
  136. this.gender = 0
  137. this.birthDate = null
  138. this.bio = ''
  139. this.city = ''
  140. this.tags = ''
  141. this.lookingFor = 0
  142. this.profileComplete = false
  143. this.birthYear = 0
  144. this.birthMonth = 0
  145. this.birthDay = 0
  146. uni.removeStorageSync('token')
  147. }
  148. }
  149. })