wecomLogin.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import { wecomAuth } from "@/api/indexAI";
  2. import router from '@/router';
  3. import md5 from 'js-md5';
  4. // 企业微信登录相关类型定义(无变化,无需可选链)
  5. export interface GuidInfo {
  6. guid: string;
  7. time: number; // 时间戳(毫秒)
  8. }
  9. export interface LastCode {
  10. code: string;
  11. }
  12. // 登录中状态锁(避免并发登录请求)
  13. let isLogging = false;
  14. /**
  15. * 校验是否已登录(AIToken 存在 + guidInfo 未过期)
  16. * @returns {boolean} 是否已登录
  17. */
  18. export const checkLoginStatus = (): boolean => {
  19. const AIToken = window.localStorage.getItem('AIToken');
  20. const guidInfoStr = window.localStorage.getItem('guidInfo');
  21. if (!AIToken) return false;
  22. if (!guidInfoStr) return false;
  23. return true;
  24. // try {
  25. // const guidInfo: GuidInfo = JSON.parse(guidInfoStr);
  26. // const currentTime = new Date().getTime();
  27. // const expireTime = 1000 * 60 * 60 * 12; // 12 小时过期
  28. // return guidInfo && guidInfo.guid && (currentTime - guidInfo.time) < expireTime;
  29. // } catch (error) {
  30. // return false;
  31. // }
  32. };
  33. /**
  34. * 初始化 guidInfo
  35. */
  36. export const initGuidInfo = (): void => {
  37. let guidInfo = JSON.parse(window.localStorage.getItem("guidInfo"));
  38. const currentTime = new Date().getTime();
  39. const guid = getGuid();
  40. const guidTime = new Date().getTime();
  41. const newGuidInfo: GuidInfo = { guid, time: guidTime };
  42. // 有缓存但过期-存储
  43. if (guidInfo && (currentTime - guidInfo.time) >= (1000 * 60 * 60 * 12)) {
  44. window.localStorage.setItem("guidInfo", JSON.stringify(newGuidInfo));
  45. window.localStorage.removeItem('lastCode');
  46. } else if (!guidInfo) {
  47. // 无缓存-存储
  48. window.localStorage.setItem('guidInfo', JSON.stringify(newGuidInfo));
  49. }
  50. };
  51. /**
  52. * 企业微信登录核心流程(获取 code → 兑换 AIToken)
  53. * @param code 企业微信授权返回的 code
  54. * @returns {Promise<void>}
  55. */
  56. export const doWecomLogin = async (code: string, wxId?: string): Promise<void> => {
  57. if (isLogging) return; // 正在登录中,忽略重复调用
  58. isLogging = true;
  59. try {
  60. const lastCodeStr = window.localStorage.getItem('lastCode');
  61. const lastCode: LastCode | null = lastCodeStr ? JSON.parse(lastCodeStr) : null;
  62. // 同一 code 不重复请求
  63. if (lastCode && lastCode.code === code) {
  64. isLogging = false;
  65. return;
  66. }
  67. // 存储当前 code,避免重复使用
  68. window.localStorage.setItem('lastCode', JSON.stringify({ code }));
  69. const formData = new FormData();
  70. formData.append('code', code);
  71. if (wxId) {
  72. formData.append("wxId", wxId);
  73. } else {
  74. formData.append("wxId", process.env.VUE_APP_APPID);
  75. }
  76. // 调用接口兑换 AIToken
  77. const res = await wecomAuth(formData);
  78. if (res && res.StatusCode === 200 && res.Data && res.Data.token) {
  79. const userInfoV1 = JSON.stringify(res.Data);
  80. window.localStorage.setItem('userInfoV1', userInfoV1);
  81. window.localStorage.setItem('AIToken', res.Data.token);
  82. window.localStorage.setItem('isRefreshProvider', res.Data.isRefreshProvider);
  83. window.localStorage.setItem('weChat', res.Data.weChat);
  84. // 处理身份,身份可能为多个,只能按身份权限大小来固定为某个身份;
  85. // if (res.Data.userType) {
  86. // // 使用位运算
  87. // const ROLE_CONFIG = [
  88. // { bit: 1 << 0, code: 'ssb', desc: '经销商' }, // 1
  89. // { bit: 1 << 1, code: 'hbs', desc: '立邦用户' }, // 2
  90. // { bit: 1 << 2, code: 'stoneLikePaint', desc: '服务商' }, // 4
  91. // { bit: 1 << 3, code: 'goldShop', desc: '金牌店' }, // 8
  92. // { bit: 1 << 4, code: 'dg', desc: '导购' }, // 16
  93. // { bit: 1 << 5, code: 'xlskf', desc: '新零售客服' } // 32
  94. // ];
  95. // const userType = Number(res.Data.userType);
  96. // console.log("userType=", userType);
  97. // const matchRole = ROLE_CONFIG.find(item => (userType & item.bit) === item.bit);
  98. // console.log("matchRole=", matchRole)
  99. // const agent = matchRole && matchRole.code || 'xlskf';
  100. // window.localStorage.setItem('agentFrom', agent);
  101. // window.localStorage.setItem('agentFromAI', agent);
  102. // }else{
  103. window.localStorage.setItem('agentFrom', 'xlskf');
  104. window.localStorage.setItem('agentFromAI', 'xlskf');
  105. // }
  106. isLogging = false;
  107. } else {
  108. isLogging = false;
  109. }
  110. } catch (error) {
  111. isLogging = false;
  112. console.error('企业微信登录失败:', error);
  113. // 登录失败可提示用户或重试
  114. // alert('登录失败,请刷新页面重试');
  115. }
  116. };
  117. export const getGuid = () => {
  118. return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
  119. let r = Math.random() * 16 | 0,
  120. v = c == 'x' ? r : (r & 0x3 | 0x8);
  121. return v.toString(16);
  122. });
  123. }
  124. export const getQyCode = () => {
  125. window.localStorage.clear();
  126. let url, appid, agentid;
  127. // url = encodeURIComponent(process.env.VUE_APP_AUTHURL);
  128. url = encodeURIComponent(window.location.href);
  129. // console.log("-url=",url)
  130. appid = process.env.VUE_APP_APPID;
  131. agentid = process.env.VUE_APP_AGENTID;
  132. window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${url}&response_type=code&scope=snsapi_base&state=&agentid=${agentid}&t=${new Date().getTime()}#wechat_redirect`;
  133. }
  134. /**
  135. * 生成指定规则的MD5签名
  136. * @param nonce 随机串 (如:8a2d9f7c3b5e10s6g9h2j4k8l0z7x5c3v)
  137. * @param timestamp 时间戳 (如:1736000000000)
  138. * @param wxId wx标识 (如:zhangsan123)
  139. * @returns 32位大写MD5加密字符串
  140. */
  141. export const getMD5 = (nonce: string, timestamp: string, wxId: string): string => {
  142. // 严格按规则拼接参数字符串:nonce=xxx&timestamp=xxx&wxId=xxx&key=xxx
  143. const signStr = `nonce=${nonce}&timestamp=${timestamp}&wxId=${wxId}&key=${process.env.VUE_APP_SECRETKEY}`;
  144. const md5Result = md5(signStr);
  145. return md5Result;
  146. };
  147. // utils/authLock.js
  148. export const authLock = {
  149. // 内存锁:实时判断
  150. isAuthorizing: false,
  151. // 初始化:从本地缓存恢复锁状态(防止页面刷新后锁失效)
  152. init() {
  153. const lock = window.localStorage.getItem('isWechatAuthorizing');
  154. this.isAuthorizing = lock === 'true';
  155. },
  156. // 加锁:同时更新内存和本地缓存
  157. lock() {
  158. this.isAuthorizing = true;
  159. window.localStorage.setItem('isWechatAuthorizing', 'true');
  160. },
  161. // 解锁:同时更新内存和本地缓存
  162. unlock() {
  163. this.isAuthorizing = false;
  164. window.localStorage.removeItem('isWechatAuthorizing');
  165. }
  166. };
  167. // 初始化锁
  168. authLock.init();