member.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. // 会员体系管理工具类
  2. class MemberService {
  3. constructor() {
  4. this.listeners = []; // 数据变化监听器
  5. this.MEMBER_LEVELS = {
  6. NORMAL: {
  7. name: '普通会员',
  8. threshold: 0,
  9. discount: 1.0,
  10. color: '#999999',
  11. icon: '👤'
  12. },
  13. SILVER: {
  14. name: '白银会员',
  15. threshold: 1000,
  16. discount: 0.98,
  17. color: '#C0C0C0',
  18. icon: '🥈'
  19. },
  20. GOLD: {
  21. name: '黄金会员',
  22. threshold: 2000,
  23. discount: 0.96,
  24. color: '#FFD700',
  25. icon: '🥇'
  26. }
  27. };
  28. this.POINTS_PRODUCTS = [
  29. {
  30. id: 'coupon_3',
  31. name: '3元优惠券',
  32. points: 288,
  33. value: 3,
  34. description: '购票时可直接抵扣3元',
  35. validDays: 30,
  36. icon: '🎫'
  37. },
  38. {
  39. id: 'coupon_6',
  40. name: '6元优惠券',
  41. points: 529,
  42. value: 6,
  43. description: '购票时可直接抵扣6元',
  44. validDays: 30,
  45. icon: '🎟️'
  46. }
  47. ];
  48. }
  49. // 获取用户会员信息
  50. getUserMemberInfo() {
  51. const memberInfo = wx.getStorageSync('memberInfo') || {
  52. memberValue: 0,
  53. points: 0,
  54. level: 'NORMAL',
  55. joinDate: new Date().toISOString(),
  56. totalSpent: 0,
  57. pointsHistory: [],
  58. coupons: []
  59. };
  60. // 更新会员等级
  61. memberInfo.level = this.calculateMemberLevel(memberInfo.memberValue);
  62. return memberInfo;
  63. }
  64. // 保存用户会员信息
  65. saveMemberInfo(memberInfo) {
  66. wx.setStorageSync('memberInfo', memberInfo);
  67. // 通知所有监听器数据已更新
  68. this.notifyListeners(memberInfo);
  69. }
  70. // 添加数据变化监听器
  71. addListener(callback) {
  72. this.listeners.push(callback);
  73. }
  74. // 移除监听器
  75. removeListener(callback) {
  76. const index = this.listeners.indexOf(callback);
  77. if (index > -1) {
  78. this.listeners.splice(index, 1);
  79. }
  80. }
  81. // 通知所有监听器
  82. notifyListeners(memberInfo) {
  83. this.listeners.forEach(callback => {
  84. try {
  85. callback(memberInfo);
  86. } catch (error) {
  87. console.error('监听器回调执行失败:', error);
  88. }
  89. });
  90. }
  91. // 计算会员等级
  92. calculateMemberLevel(memberValue) {
  93. if (memberValue >= this.MEMBER_LEVELS.GOLD.threshold) {
  94. return 'GOLD';
  95. } else if (memberValue >= this.MEMBER_LEVELS.SILVER.threshold) {
  96. return 'SILVER';
  97. } else {
  98. return 'NORMAL';
  99. }
  100. }
  101. // 获取会员等级信息
  102. getMemberLevelInfo(level) {
  103. return this.MEMBER_LEVELS[level] || this.MEMBER_LEVELS.NORMAL;
  104. }
  105. // 消费时增加会员值和积分
  106. addMemberValueAndPoints(amount) {
  107. const memberInfo = this.getUserMemberInfo();
  108. const oldLevel = memberInfo.level;
  109. // 消费一元累计会员值一点和积分一分
  110. const addedValue = Math.floor(amount);
  111. const addedPoints = Math.floor(amount);
  112. memberInfo.memberValue += addedValue;
  113. memberInfo.points += addedPoints;
  114. memberInfo.totalSpent += amount;
  115. // 添加积分历史记录
  116. memberInfo.pointsHistory.unshift({
  117. type: 'earn',
  118. amount: addedPoints,
  119. description: `消费获得积分`,
  120. date: new Date().toISOString(),
  121. orderId: Date.now().toString()
  122. });
  123. // 检查等级变化
  124. const newLevel = this.calculateMemberLevel(memberInfo.memberValue);
  125. let levelUpgraded = false;
  126. if (oldLevel !== newLevel) {
  127. levelUpgraded = true;
  128. memberInfo.level = newLevel;
  129. // 添加升级记录
  130. memberInfo.pointsHistory.unshift({
  131. type: 'upgrade',
  132. amount: 0,
  133. description: `恭喜升级为${this.MEMBER_LEVELS[newLevel].name}`,
  134. date: new Date().toISOString()
  135. });
  136. }
  137. this.saveMemberInfo(memberInfo);
  138. return {
  139. memberInfo,
  140. addedValue,
  141. addedPoints,
  142. levelUpgraded,
  143. newLevel: levelUpgraded ? newLevel : null
  144. };
  145. }
  146. // 使用积分兑换商品
  147. exchangePoints(productId) {
  148. const memberInfo = this.getUserMemberInfo();
  149. const product = this.POINTS_PRODUCTS.find(p => p.id === productId);
  150. if (!product) {
  151. return { success: false, message: '商品不存在' };
  152. }
  153. if (memberInfo.points < product.points) {
  154. return { success: false, message: '积分不足' };
  155. }
  156. // 扣除积分
  157. memberInfo.points -= product.points;
  158. // 添加优惠券
  159. const coupon = {
  160. id: Date.now().toString(),
  161. name: product.name,
  162. value: product.value,
  163. used: false,
  164. expireDate: new Date(Date.now() + product.validDays * 24 * 60 * 60 * 1000).toISOString(),
  165. createDate: new Date().toISOString()
  166. };
  167. memberInfo.coupons.push(coupon);
  168. // 添加积分使用记录
  169. memberInfo.pointsHistory.unshift({
  170. type: 'exchange',
  171. amount: -product.points,
  172. description: `兑换${product.name}`,
  173. date: new Date().toISOString(),
  174. couponId: coupon.id
  175. });
  176. this.saveMemberInfo(memberInfo);
  177. return {
  178. success: true,
  179. message: '兑换成功',
  180. coupon,
  181. memberInfo
  182. };
  183. }
  184. // 获取可用优惠券
  185. getAvailableCoupons() {
  186. const memberInfo = this.getUserMemberInfo();
  187. const now = new Date();
  188. return memberInfo.coupons.filter(coupon =>
  189. !coupon.used && new Date(coupon.expireDate) > now
  190. );
  191. }
  192. // 使用优惠券
  193. useCoupon(couponId) {
  194. const memberInfo = this.getUserMemberInfo();
  195. const coupon = memberInfo.coupons.find(c => c.id === couponId);
  196. if (!coupon || coupon.used || new Date(coupon.expireDate) <= new Date()) {
  197. return { success: false, message: '优惠券无效或已过期' };
  198. }
  199. coupon.used = true;
  200. coupon.usedDate = new Date().toISOString();
  201. this.saveMemberInfo(memberInfo);
  202. return { success: true, coupon };
  203. }
  204. // 获取会员折扣
  205. getMemberDiscount(level = null) {
  206. const memberInfo = this.getUserMemberInfo();
  207. const currentLevel = level || memberInfo.level;
  208. return this.MEMBER_LEVELS[currentLevel].discount;
  209. }
  210. // 清理过期积分(每年过期)
  211. cleanExpiredPoints() {
  212. const memberInfo = this.getUserMemberInfo();
  213. const oneYearAgo = new Date(Date.now() - 365 * 24 * 60 * 60 * 1000);
  214. // 保留一年内的积分历史
  215. const validHistory = memberInfo.pointsHistory.filter(h =>
  216. new Date(h.date) > oneYearAgo
  217. );
  218. // 重新计算积分(只计算一年内的收入积分)
  219. const validPoints = validHistory.reduce((total, h) => {
  220. if (h.type === 'earn' && h.amount > 0) {
  221. return total + h.amount;
  222. } else if (h.type === 'exchange' && h.amount < 0) {
  223. return total + h.amount; // h.amount 已经是负数
  224. }
  225. return total;
  226. }, 0);
  227. memberInfo.points = Math.max(0, validPoints);
  228. memberInfo.pointsHistory = validHistory;
  229. this.saveMemberInfo(memberInfo);
  230. return memberInfo;
  231. }
  232. // 获取下一等级信息
  233. getNextLevelInfo(currentLevel) {
  234. switch (currentLevel) {
  235. case 'NORMAL':
  236. return {
  237. nextLevel: 'SILVER',
  238. nextLevelInfo: this.MEMBER_LEVELS.SILVER,
  239. needed: this.MEMBER_LEVELS.SILVER.threshold
  240. };
  241. case 'SILVER':
  242. return {
  243. nextLevel: 'GOLD',
  244. nextLevelInfo: this.MEMBER_LEVELS.GOLD,
  245. needed: this.MEMBER_LEVELS.GOLD.threshold
  246. };
  247. case 'GOLD':
  248. return {
  249. nextLevel: null,
  250. nextLevelInfo: null,
  251. needed: 0
  252. };
  253. default:
  254. return null;
  255. }
  256. }
  257. }
  258. // 导出单例
  259. const memberService = new MemberService();
  260. export default memberService;