index.js 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701
  1. // var Parse = getApp().Parse;
  2. // var app = getApp()
  3. // const { wxLogin } = require('./utils/login')
  4. const CONFIG = require("config.js");
  5. let config = {
  6. appid: CONFIG.default.appid,
  7. company: CONFIG.default.company,
  8. rootPage: CONFIG.default.rootPage,
  9. }
  10. const plugin = requirePlugin('fm-plugin')
  11. const { Parse, checkAuth } = plugin
  12. Page({
  13. /**
  14. * 页面的初始数据
  15. */
  16. data: {
  17. splashUrl: wx.getStorageSync("enabledOptions")[0],
  18. loading:true
  19. },
  20. /**
  21. * 生命周期函数--监听页面加载
  22. */
  23. onLoad: async function (options) {
  24. // 拦截插件的"登录信息已过期"提示
  25. const originalShowModal = wx.showModal;
  26. wx.showModal = function(options) {
  27. // 如果是"登录信息已过期"的提示,改为友好的重新登录提示
  28. if (options.content && options.content.includes('登录信息过期')) {
  29. console.log('⚠️ 拦截到"登录信息已过期"提示,改为友好提示');
  30. return originalShowModal.call(this, {
  31. title: '提示',
  32. content: '登录状态异常,是否重新登录?',
  33. showCancel: true,
  34. cancelText: '取消',
  35. confirmText: '重新登录',
  36. success: (result) => {
  37. if (result.confirm) {
  38. // 清除登录状态
  39. wx.removeStorageSync("sessionToken");
  40. wx.removeStorageSync("userLogin");
  41. // 重新加载首页
  42. const rootPage = getApp().globalData.rootPage || getApp().globalData.defaultTabBar?.list?.[0]?.pagePath || '/pages/index/index';
  43. wx.reLaunch({
  44. url: rootPage
  45. });
  46. }
  47. // 调用原始的 success 回调
  48. if (options.success) {
  49. options.success(result);
  50. }
  51. }
  52. });
  53. }
  54. // 其他提示正常显示
  55. return originalShowModal.call(this, options);
  56. };
  57. wx.login({
  58. success: function (res) {
  59. if (res.code) {
  60. console.log(res);
  61. // wx.request({
  62. // url: "https://server.fmode.cn/api/wxapp/auth_wxapp",
  63. // data: {
  64. // c: getApp().globalData.company,
  65. // code: res.code,
  66. // },
  67. // async success(res) {
  68. // wx.setStorageSync("userInfo", res.data);
  69. // resolve(res)
  70. // },
  71. // });
  72. }
  73. },
  74. fail: function (err) {
  75. wx.showToast({
  76. title: '服务器繁忙,请稍后重试',
  77. })
  78. }
  79. });
  80. wx.setStorageSync("invite", null);
  81. // 处理扫码链接(options.q 包含完整的URL或activityId)
  82. if (options.q) {
  83. let str = decodeURIComponent(options.q); // 扫描二维码获得的跳转链接
  84. // 兼容一些环境中把 & 编码成 & 的情况,防止参数解析错误(如 scanCount=0&storeId=...)
  85. if (str.indexOf('&') !== -1) {
  86. console.log('🔧 检测到 URL 中包含 &,自动还原为 &');
  87. str = str.replace(/&/g, '&');
  88. }
  89. // 检查是否是员工邀请链接(app.fmode.cn/dev/pobingfeng/manager/staff?invite=xxx)
  90. if (str.includes('app.fmode.cn/dev/pobingfeng/manager/staff')) {
  91. let obj = this.getParaName(str);
  92. if (obj && obj.invite) {
  93. wx.setStorageSync("invite", obj.invite);
  94. console.log('✅ 检测到员工邀请链接,invite:', obj.invite);
  95. }
  96. // 员工邀请链接不需要跳转到店铺,直接返回
  97. this.setData({ options: options });
  98. plugin.init(config, wx.getStorageSync('invite'));
  99. return;
  100. }
  101. // 检查是否是活动海报二维码(activityId作为qrCode参数值)
  102. // 如果str不包含http/https,且看起来像是一个ID,则可能是activityId
  103. if (!str.includes('http://') && !str.includes('https://') && !str.includes('?')) {
  104. // 可能是活动ID,尝试作为activityId处理
  105. if (str && str.length > 0 && !isNaN(str)) {
  106. options.activityId = str;
  107. console.log('✅ 检测到活动海报二维码,activityId:', str);
  108. // 活动二维码需要特殊处理,先保存activityId
  109. wx.setStorageSync("activityId", str);
  110. }
  111. }
  112. // 处理店铺相关的二维码链接(pwa.fmode.cn/gomini/pmd/)
  113. // 从完整URL中提取参数
  114. if (str.includes('pwa.fmode.cn/gomini/pmd') || str.includes('?')) {
  115. let obj = this.getParaName(str);
  116. if (obj && obj.invite) {
  117. wx.setStorageSync("invite", obj.invite);
  118. }
  119. // 将所有参数合并到 options 中
  120. obj && Object.keys(obj).forEach(key=> options[key] = obj[key]);
  121. }
  122. }
  123. // 兼容从编译参数或页面直达传入的 storeId
  124. if (options && options.scene && !options.storeId) {
  125. try {
  126. const sceneStr = decodeURIComponent(options.scene)
  127. if (sceneStr) {
  128. const pairs = sceneStr.split('&')
  129. for (const p of pairs) {
  130. const [k, v] = p.split('=')
  131. if (k === 'storeId' && v) {
  132. options.storeId = v
  133. break
  134. }
  135. }
  136. }
  137. } catch (e) {
  138. console.warn('解析 scene 失败:', e)
  139. }
  140. }
  141. this.setData({
  142. options: options
  143. })
  144. let {
  145. time,
  146. dramaId,
  147. roomId,
  148. orderId,
  149. shopId,
  150. invite,
  151. activityId,
  152. company,
  153. inviteHost,
  154. storeId
  155. } = options
  156. time && wx.setStorageSync("time", time);
  157. dramaId && wx.setStorageSync("dramaId", dramaId);
  158. roomId && wx.setStorageSync("roomId", roomId);
  159. orderId && wx.setStorageSync("orderId", orderId);
  160. shopId && wx.setStorageSync("shopId", shopId);
  161. invite && wx.setStorageSync("invite", invite);
  162. activityId && wx.setStorageSync("activityId", activityId);
  163. inviteHost && wx.setStorageSync("inviteHost", true);
  164. if (storeId) {
  165. // 扫码进入时,强制设置店铺 ID,标记为扫码来源
  166. wx.setStorageSync('storeId', storeId)
  167. getApp().globalData.storeId = storeId
  168. wx.setStorageSync('storeId_from_scan', true)
  169. console.log('✅ 入口页扫码进入,已设置店铺 ID(优先级最高):', storeId)
  170. }
  171. if (company) getApp().globalData.toCompany = true;
  172. // 检查是否是扫码进入(需要统计扫码次数)
  173. this.checkAndHandleScan(options);
  174. plugin.init(config, wx.getStorageSync('invite'))
  175. },
  176. /**
  177. * 生命周期函数--监听页面初次渲染完成
  178. */
  179. onReady: async function () { },
  180. /**
  181. * 生命周期函数--监听页面显示
  182. */
  183. onShow: async function () {
  184. await this.review()
  185. },
  186. async review(force){
  187. try {
  188. let options = this.data.options
  189. let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath
  190. if (options) {
  191. let objArr = Object.keys(options)
  192. if (objArr && objArr.length > 0) {
  193. let parms = '?'
  194. objArr.forEach((o, index) => {
  195. if (index > 0) {
  196. parms += '&' + o + '=' + options[o]
  197. } else {
  198. parms += o + '=' + options[o]
  199. }
  200. })
  201. url += parms
  202. }
  203. }
  204. let currentUser = Parse.User.current()
  205. console.log('===========================================');
  206. console.log('======= index.js review 方法 =======');
  207. console.log('当前用户:', currentUser ? currentUser.id : '无');
  208. console.log('用户手机号:', currentUser?.get('mobile') || '无');
  209. console.log('用户名:', currentUser?.get('username') || '无');
  210. console.log('Session Token:', currentUser?.getSessionToken()?.substring(0, 20) || '无');
  211. console.log('userLogin 存储:', wx.getStorageSync('userLogin') || '无');
  212. console.log('force 参数:', force);
  213. console.log('===========================================');
  214. // 查询 Company 的 isPublishing 字段
  215. let isPublishing = false;
  216. try {
  217. const companyQuery = new Parse.Query('Company');
  218. companyQuery.equalTo('objectId', getApp().globalData.company);
  219. companyQuery.select('isPublishing');
  220. try {
  221. const companyObj = await companyQuery.first();
  222. if (companyObj) {
  223. isPublishing = companyObj.get('isPublishing') === true;
  224. console.log('📋 Company isPublishing:', isPublishing);
  225. } else {
  226. console.log('⚠️ 未找到 Company 记录,默认 isPublishing = false');
  227. isPublishing = false;
  228. }
  229. } catch (queryError) {
  230. console.error('❌ 查询 Company 失败(可能 token 无效):', queryError.message);
  231. // 查询失败时,默认为 false(强制登录)
  232. isPublishing = false;
  233. }
  234. // 保存到全局,供其他页面使用
  235. getApp().globalData.isPublishing = isPublishing;
  236. wx.setStorageSync('isPublishing', isPublishing);
  237. } catch (error) {
  238. console.error('❌ 查询 Company isPublishing 失败:', error);
  239. // 查询失败时,默认为 false(强制登录)
  240. isPublishing = false;
  241. }
  242. // 根据 isPublishing 决定是否强制登录
  243. if (!currentUser || force) {
  244. console.log('🔄 开始调用 checkAuth...');
  245. // isPublishing == true 时不强制授权,否则强制授权
  246. const forceAuth = !isPublishing;
  247. console.log('🔐 是否强制登录:', forceAuth);
  248. let r = await checkAuth(forceAuth);
  249. console.log('===========================================');
  250. console.log('======= checkAuth 返回结果 =======');
  251. console.log('返回值:', r);
  252. // 重新获取用户信息
  253. currentUser = Parse.User.current();
  254. console.log('checkAuth 后的用户:', currentUser ? currentUser.id : '无');
  255. console.log('checkAuth 后的手机号:', currentUser?.get('mobile') || '无');
  256. console.log('checkAuth 后的 Session Token:', currentUser?.getSessionToken()?.substring(0, 20) || '无');
  257. console.log('===========================================');
  258. // 如果强制登录但用户未登录,不允许继续访问
  259. if (forceAuth && !r) {
  260. console.log('❌ 强制登录模式,用户未登录,停止访问');
  261. return;
  262. }
  263. // 即使登录失败,也允许继续访问(仅在非强制登录模式下)
  264. if(!r) {
  265. console.log('⚠️ 用户未登录或拒绝授权,允许游客访问');
  266. // 不要 return,继续执行后面的跳转逻辑
  267. } else {
  268. // 登录成功,设置 userLogin
  269. if (currentUser && currentUser.get('mobile')) {
  270. wx.setStorageSync("userLogin", currentUser.id);
  271. console.log('✅ 授权登录成功,已设置 userLogin:', currentUser.id);
  272. console.log('✅ 用户手机号:', currentUser.get('mobile'));
  273. } else {
  274. console.warn('⚠️ checkAuth 返回成功,但用户没有手机号!');
  275. console.warn(' 用户对象:', currentUser);
  276. }
  277. // 检查是否有待记录的扫码信息
  278. await this.checkAndRecordPendingScan();
  279. // 如果用户没有来源信息,且当前有扫码参数,记录来源
  280. await this.checkAndRecordUserSourceOnLogin();
  281. }
  282. } else {
  283. console.log('✅ 用户已登录,跳过 checkAuth');
  284. // 用户已登录,确保 userLogin 已设置
  285. if (currentUser.get('mobile')) {
  286. wx.setStorageSync("userLogin", currentUser.id);
  287. console.log('✅ 已确认 userLogin:', currentUser.id);
  288. }
  289. this.updateUser(currentUser.id);
  290. // 用户已登录,检查是否有待记录的扫码信息
  291. await this.checkAndRecordPendingScan();
  292. // 如果用户没有来源信息,且当前有扫码参数,记录来源
  293. await this.checkAndRecordUserSourceOnLogin();
  294. }
  295. getApp().Parse = Parse
  296. getApp().checkAuth = checkAuth
  297. if (!await this.getCompanyServerExpire(url)) {
  298. return
  299. }
  300. // 检查是否需要跳转到活动页面
  301. if (wx.getStorageSync('need_activity_redirect') === true) {
  302. await this.redirectToActivityPage();
  303. return;
  304. }
  305. // 检查是否需要跳转到扫码统计页面
  306. if (this.shouldRedirectToScanPage()) {
  307. await this.redirectToScanPage();
  308. return;
  309. }
  310. console.log('✅ 准备跳转到:', url);
  311. wx.redirectTo({
  312. url: url,
  313. success: () => {
  314. console.log('✅ redirectTo 跳转成功');
  315. },
  316. fail: (err) => {
  317. console.error('❌ redirectTo 失败:', err);
  318. console.log('⚠️ 尝试使用 reLaunch');
  319. // 降级:尝试使用 reLaunch
  320. wx.reLaunch({
  321. url: url,
  322. success: () => {
  323. console.log('✅ reLaunch 跳转成功');
  324. },
  325. fail: (err2) => {
  326. console.error('❌ reLaunch 也失败:', err2);
  327. // 显示错误提示
  328. this.setData({ loading: false });
  329. wx.showModal({
  330. title: '温馨提示',
  331. content: '页面加载失败,请检查网络后重试。',
  332. showCancel: true,
  333. cancelText: '退出',
  334. confirmText: '重试',
  335. success: (result) => {
  336. if (result.confirm) {
  337. this.review(true);
  338. } else {
  339. wx.exitMiniProgram();
  340. }
  341. }
  342. });
  343. }
  344. });
  345. }
  346. });
  347. }
  348. catch (err) {
  349. console.log('❌ review 方法出错:', err);
  350. /* 登录身份信息到期,重新登陆 */
  351. if((err?.message?.indexOf('Session token is expired') != -1 || err?.message?.indexOf('Invalid session token') != -1) && !force){
  352. console.log('⚠️ Session Token 过期,准备重新登录');
  353. // 保存需要保留的数据
  354. const invite = wx.getStorageSync('invite');
  355. const agreementAccepted = wx.getStorageSync('user_agreement_accepted');
  356. const storeId = wx.getStorageSync('storeId');
  357. const isPublishing = wx.getStorageSync('isPublishing');
  358. // 先登出 Parse 用户
  359. try {
  360. await Parse.User.logOut();
  361. console.log('✅ Parse 用户已登出');
  362. } catch (logoutErr) {
  363. console.warn('⚠️ Parse 登出失败:', logoutErr);
  364. }
  365. // 清除所有存储
  366. wx.clearStorageSync();
  367. // 恢复需要保留的数据
  368. if (invite) wx.setStorageSync('invite', invite);
  369. if (agreementAccepted) wx.setStorageSync('user_agreement_accepted', agreementAccepted);
  370. if (storeId) wx.setStorageSync('storeId', storeId);
  371. if (isPublishing !== undefined) wx.setStorageSync('isPublishing', isPublishing);
  372. console.log('✅ 已清除过期登录信息,保留必要数据');
  373. console.log(' 保留的 invite:', invite || '无');
  374. console.log(' 保留的协议状态:', agreementAccepted || '无');
  375. console.log(' 保留的店铺ID:', storeId || '无');
  376. /* 强制重新登录 */
  377. this.review(true);
  378. return;
  379. }
  380. // 如果出错,尝试继续跳转到主页(游客模式)
  381. console.log('⚠️ 登录出错,尝试游客模式访问');
  382. let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath;
  383. if (this.data.options) {
  384. let objArr = Object.keys(this.data.options)
  385. if (objArr && objArr.length > 0) {
  386. let parms = '?'
  387. objArr.forEach((o, index) => {
  388. if (index > 0) {
  389. parms += '&' + o + '=' + this.data.options[o]
  390. } else {
  391. parms += o + '=' + this.data.options[o]
  392. }
  393. })
  394. url += parms
  395. }
  396. }
  397. wx.redirectTo({
  398. url: url,
  399. fail: () => {
  400. // 如果跳转失败,显示错误提示
  401. this.setData({
  402. loading:false
  403. })
  404. wx.showModal({
  405. title: '温馨提示',
  406. content: '页面加载失败,请检查网络后重试。',
  407. showCancel: true,
  408. cancelText: '退出',
  409. confirmText: '重试',
  410. success: (result) => {
  411. if (result.confirm) {
  412. // 用户选择重试
  413. this.review(true)
  414. } else {
  415. // 用户选择退出
  416. wx.exitMiniProgram()
  417. }
  418. },
  419. });
  420. }
  421. });
  422. }
  423. },
  424. /**
  425. * 检查并记录待处理的扫码统计
  426. * 同时记录用户来源信息到 _User 表
  427. */
  428. async checkAndRecordPendingScan() {
  429. try {
  430. const pendingScan = wx.getStorageSync('pending_scan_record');
  431. if (!pendingScan) {
  432. return;
  433. }
  434. console.log('===========================================');
  435. console.log('======= 发现待记录的扫码信息 =======');
  436. console.log('扫码信息:', pendingScan);
  437. console.log('===========================================');
  438. // 检查是否超时(24小时)
  439. const now = Date.now();
  440. const scanTime = pendingScan.timestamp || 0;
  441. const hoursPassed = (now - scanTime) / (1000 * 60 * 60);
  442. if (hoursPassed > 24) {
  443. console.log('⚠️ 扫码信息已超过24小时,不再记录');
  444. wx.removeStorageSync('pending_scan_record');
  445. return;
  446. }
  447. // 记录用户来源信息
  448. await this.recordUserSource(pendingScan);
  449. // 记录扫码统计
  450. await this.recordScanStatistics({
  451. storeId: pendingScan.storeId,
  452. sourceType: pendingScan.sourceType,
  453. sourceId: pendingScan.sourceId,
  454. ownerId: pendingScan.ownerId,
  455. employeeId: pendingScan.employeeId,
  456. partnerId: pendingScan.partnerId,
  457. userId: pendingScan.userId,
  458. productId: pendingScan.productId,
  459. scanCount: pendingScan.scanCount
  460. });
  461. // 清除待记录的扫码信息
  462. wx.removeStorageSync('pending_scan_record');
  463. console.log('✅ 扫码统计已记录并清除');
  464. } catch (error) {
  465. console.error('❌ 记录待处理扫码信息失败:', error);
  466. }
  467. },
  468. /**
  469. * 记录用户来源信息到 _User 表
  470. * 根据扫码参数判断来源类型并保存到用户的 source 字段
  471. *
  472. * 来源类型:
  473. * 1. 渠道xxx→异业xxx(老板后台添加的异业)
  474. * 2. 渠道xxx→异业xxx→员工xxx(员工后台添加的异业)
  475. * 3. 员工xxx
  476. * 4. 老板
  477. * 5. 自主进入(无任何推荐)
  478. */
  479. async recordUserSource(scanInfo) {
  480. try {
  481. const currentUser = Parse.User.current();
  482. if (!currentUser) {
  483. console.log('⚠️ 用户未登录,无法记录来源');
  484. return;
  485. }
  486. // 如果用户已经有来源信息,不再覆盖(首次来源原则)
  487. const existingSource = currentUser.get('source');
  488. if (existingSource) {
  489. console.log('ℹ️ 用户已有来源信息,不覆盖:', existingSource);
  490. return;
  491. }
  492. console.log('📊 ===========================================');
  493. console.log('📊 [记录来源] 开始记录用户来源信息');
  494. console.log('📊 用户ID:', currentUser.id);
  495. console.log('📊 ===========================================');
  496. const { partnerId, employeeId, ownerId, userId, storeId } = scanInfo;
  497. let sourceInfo = {
  498. type: 'self_entry', // 默认为自主进入
  499. label: '自主进入',
  500. timestamp: new Date(),
  501. storeId: storeId
  502. };
  503. // 判断来源类型
  504. if (partnerId && employeeId) {
  505. // 情况2: 渠道xxx→异业xxx→员工xxx(员工后台添加的异业)
  506. console.log('🔍 [来源类型] 渠道→异业→员工');
  507. try {
  508. // 查询异业信息
  509. const partnerQuery = new Parse.Query('Partner');
  510. const partner = await partnerQuery.get(partnerId);
  511. const partnerName = partner.get('name') || '未知异业';
  512. const channelName = partner.get('channelName') || partner.get('category') || '未知渠道';
  513. // 查询员工信息
  514. const employeeQuery = new Parse.Query('Employee');
  515. const employee = await employeeQuery.get(employeeId);
  516. const employeeName = employee.get('name') || '未知员工';
  517. sourceInfo = {
  518. type: 'channel_partner_employee',
  519. label: `${channelName},${partnerName},员工${employeeName}`,
  520. channelName: channelName,
  521. partnerId: partnerId,
  522. partnerName: partnerName,
  523. employeeId: employeeId,
  524. employeeName: employeeName,
  525. timestamp: new Date(),
  526. storeId: storeId
  527. };
  528. console.log('✅ [来源信息] 渠道→异业→员工:', sourceInfo.label);
  529. } catch (error) {
  530. console.error('❌ 查询异业或员工信息失败:', error);
  531. }
  532. } else if (partnerId && !employeeId) {
  533. // 情况1: 渠道xxx→异业xxx(老板后台添加的异业)
  534. console.log('🔍 [来源类型] 渠道→异业');
  535. try {
  536. // 查询异业信息
  537. const partnerQuery = new Parse.Query('Partner');
  538. const partner = await partnerQuery.get(partnerId);
  539. const partnerName = partner.get('name') || '未知异业';
  540. const channelName = partner.get('channelName') || partner.get('category') || '未知渠道';
  541. sourceInfo = {
  542. type: 'channel_partner',
  543. label: `${channelName},${partnerName}`,
  544. channelName: channelName,
  545. partnerId: partnerId,
  546. partnerName: partnerName,
  547. timestamp: new Date(),
  548. storeId: storeId
  549. };
  550. console.log('✅ [来源信息] 渠道→异业:', sourceInfo.label);
  551. } catch (error) {
  552. console.error('❌ 查询异业信息失败:', error);
  553. }
  554. } else if (employeeId && !partnerId) {
  555. // 情况3: 员工xxx
  556. console.log('🔍 [来源类型] 员工');
  557. try {
  558. // 查询员工信息
  559. const employeeQuery = new Parse.Query('Employee');
  560. const employee = await employeeQuery.get(employeeId);
  561. const employeeName = employee.get('name') || '未知员工';
  562. sourceInfo = {
  563. type: 'employee',
  564. label: employeeName,
  565. employeeId: employeeId,
  566. employeeName: employeeName,
  567. timestamp: new Date(),
  568. storeId: storeId
  569. };
  570. console.log('✅ [来源信息] 员工:', sourceInfo.label);
  571. } catch (error) {
  572. console.error('❌ 查询员工信息失败:', error);
  573. }
  574. } else if (ownerId) {
  575. // 情况4: 老板
  576. console.log('🔍 [来源类型] 老板');
  577. sourceInfo = {
  578. type: 'owner',
  579. label: '老板',
  580. ownerId: ownerId,
  581. timestamp: new Date(),
  582. storeId: storeId
  583. };
  584. console.log('✅ [来源信息] 老板');
  585. } else if (userId) {
  586. // 推广员(如果有的话)
  587. console.log('🔍 [来源类型] 推广员');
  588. try {
  589. // 查询推广员信息
  590. const userQuery = new Parse.Query('_User');
  591. const promoter = await userQuery.get(userId);
  592. const promoterName = promoter.get('username') || promoter.get('mobile') || '未知推广员';
  593. sourceInfo = {
  594. type: 'promoter',
  595. label: `推广员${promoterName}`,
  596. userId: userId,
  597. promoterName: promoterName,
  598. timestamp: new Date(),
  599. storeId: storeId
  600. };
  601. console.log('✅ [来源信息] 推广员:', sourceInfo.label);
  602. } catch (error) {
  603. console.error('❌ 查询推广员信息失败:', error);
  604. }
  605. } else {
  606. // 情况5: 自主进入(无任何推荐)
  607. console.log('🔍 [来源类型] 自主进入');
  608. console.log('✅ [来源信息] 自主进入');
  609. }
  610. // 保存来源信息到用户
  611. currentUser.set('source', sourceInfo);
  612. await currentUser.save();
  613. console.log('✅ [保存成功] 用户来源信息已保存');
  614. console.log(' - 来源类型:', sourceInfo.type);
  615. console.log(' - 来源标签:', sourceInfo.label);
  616. console.log('📊 ===========================================');
  617. } catch (error) {
  618. console.error('❌ 记录用户来源失败:', error);
  619. }
  620. },
  621. /**
  622. * 用户登录后检查并记录来源信息
  623. * 如果用户是首次登录且没有来源信息,根据当前的扫码参数记录来源
  624. */
  625. async checkAndRecordUserSourceOnLogin() {
  626. try {
  627. const currentUser = Parse.User.current();
  628. if (!currentUser) {
  629. return;
  630. }
  631. // 如果用户已经有来源信息,不再处理
  632. const existingSource = currentUser.get('source');
  633. if (existingSource) {
  634. console.log('ℹ️ 用户已有来源信息,跳过记录');
  635. return;
  636. }
  637. // 检查是否有扫码参数
  638. const scanStoreId = wx.getStorageSync('scan_storeId');
  639. const scanPartnerId = wx.getStorageSync('scan_partnerId');
  640. const scanEmployeeId = wx.getStorageSync('scan_employeeId');
  641. const scanOwnerId = wx.getStorageSync('scan_ownerId');
  642. const scanUserId = wx.getStorageSync('scan_userId');
  643. // 如果有任何扫码参数,记录来源
  644. if (scanStoreId || scanPartnerId || scanEmployeeId || scanOwnerId || scanUserId) {
  645. console.log('📊 检测到扫码参数,记录用户来源');
  646. await this.recordUserSource({
  647. storeId: scanStoreId,
  648. partnerId: scanPartnerId,
  649. employeeId: scanEmployeeId,
  650. ownerId: scanOwnerId,
  651. userId: scanUserId
  652. });
  653. } else {
  654. // 没有任何扫码参数,标记为自主进入
  655. console.log('📊 无扫码参数,标记为自主进入');
  656. await this.recordUserSource({
  657. storeId: wx.getStorageSync('storeId') || null
  658. });
  659. }
  660. } catch (error) {
  661. console.error('❌ 检查并记录用户来源失败:', error);
  662. }
  663. },
  664. async updateUser(id) {
  665. try {
  666. let User = new Parse.Query('_User')
  667. let user = await User.get(id)
  668. let invite = wx.getStorageSync('invite')
  669. //查询邀请人user
  670. let query = new Parse.Query("_User")
  671. query.equalTo('objectId', invite)
  672. let result = await query.first()
  673. if (result && result.id && result.get("invite")?.id == user.id) {
  674. console.error('邀请人不能是自己的下级')
  675. return
  676. }
  677. if (invite && !user.get('invite') && user.id != invite && !user.get('agentLevel')) {
  678. console.log('上下级绑定成功');
  679. user.set('invite', {
  680. __type: "Pointer",
  681. className: "_User",
  682. objectId: invite
  683. })
  684. user.set('agent', {
  685. __type: "Pointer",
  686. className: "_User",
  687. objectId: invite
  688. })
  689. await Parse.Cloud.run('user_save', {
  690. userJson: user.toJSON()
  691. })
  692. }
  693. } catch (error) {
  694. console.error('❌ updateUser 失败:', error.message);
  695. // 不阻断流程,继续执行
  696. }
  697. },
  698. async getCompanyServerExpire(url) {
  699. try {
  700. let query = new Parse.Query('Company')
  701. query.equalTo('objectId', getApp().globalData.company)
  702. query.select('expireDate', 'expireMap')
  703. let com = await query.first()
  704. if (com?.id && com?.get('expireDate')) {
  705. let now = + new Date()
  706. let expireTime = + new Date(com?.get('expireDate'))
  707. if (com?.get('expireMap') && com.get('expireMap')[getApp().globalData.appid]) {
  708. expireTime = + new Date(com.get('expireMap')[getApp().globalData.appid])
  709. }
  710. if (now >= expireTime) {
  711. console.log('服务器到期');
  712. wx.reLaunch({
  713. url: `common-page/pages/loading/index?url=${url}`,
  714. });
  715. return
  716. }
  717. }
  718. return true
  719. } catch (error) {
  720. console.error('❌ getCompanyServerExpire 失败:', error.message);
  721. // 查询失败时,允许继续访问
  722. return true
  723. }
  724. },
  725. onUnload: function () {
  726. wx.setStorageSync("active", 0);
  727. },
  728. getParaName(url) {
  729. if (!url || url.indexOf('?') == -1) {
  730. return
  731. }
  732. // 兼容 URL 中 & 的情况,先统一还原为 &
  733. if (url.indexOf('&') !== -1) {
  734. url = url.replace(/&/g, '&');
  735. }
  736. // 提取查询参数部分(去除可能的 hash 部分)
  737. let queryString = url.split('?')[1];
  738. // 如果包含 #,只取 # 之前的部分
  739. if (queryString.indexOf('#') !== -1) {
  740. queryString = queryString.split('#')[0];
  741. }
  742. return this.setObject(queryString) //封装成对象
  743. },
  744. setObject(paraArr) {
  745. let obj = {}
  746. let arr1 = paraArr.split('&')
  747. arr1.forEach(item => {
  748. let str = item.split('=')
  749. let key = str[0]
  750. let val = str[1]
  751. obj[key] = val
  752. })
  753. return obj
  754. },
  755. /**
  756. * 检查并处理扫码参数
  757. * 支持所有类型的二维码:
  758. * 1. 推广员二维码: ?scanCount=0&storeId=xxx&userId=xxx
  759. * 2. 产品二维码: ?scanCount=0&storeId=xxx&productId=xxx
  760. * 3. 案例二维码: ?scanCount=0&storeId=xxx&caseId=xxx
  761. * 4. 活动海报二维码: activityId作为qrCode参数值
  762. * 5. 异业合作伙伴二维码(移动端 / PC端统一): ?scanCount=xxx&storeId=xxx&partnerId=xxx
  763. * 6. 员工邀请二维码(移动端): ?scanCount=0&storeId=xxx&employeeId=xxx
  764. * 7. 我的二维码(老板): ?scanCount=0&storeId=xxx&ownerId=xxx
  765. * 8. 我的二维码(员工): ?scanCount=0&storeId=xxx&employeeId=xxx
  766. * 9. 方案分享二维码: ?scanCount=0&storeId=xxx&planId=xxx&shareUserId=xxx
  767. */
  768. checkAndHandleScan(options) {
  769. console.log('🔍🔍🔍 ========================================');
  770. console.log('🔍 [扫码检测] 开始检查扫码参数');
  771. console.log('🔍 完整 options 对象:', JSON.stringify(options, null, 2));
  772. console.log('🔍🔍🔍 ========================================');
  773. const {
  774. scanCount,
  775. ownerId,
  776. employeeId,
  777. partnerId,
  778. storeId,
  779. productId,
  780. caseId,
  781. userId, // 推广员二维码使用userId
  782. activityId,
  783. schemeId, // 方案ID(旧参数名)
  784. planId, // 方案ID(新参数名,优先使用)
  785. shareUserId, // 分享方案的用户ID
  786. q // 扫码链接
  787. } = options;
  788. // planId 和 schemeId 兼容处理,优先使用 planId
  789. const finalPlanId = planId || schemeId;
  790. console.log('📋 [扫码参数] 提取的参数:');
  791. console.log(' - scanCount:', scanCount || '无');
  792. console.log(' - storeId:', storeId || '无');
  793. console.log(' - ownerId:', ownerId || '无');
  794. console.log(' - employeeId:', employeeId || '无');
  795. console.log(' - partnerId:', partnerId || '无');
  796. console.log(' - userId:', userId || '无');
  797. console.log(' - productId:', productId || '无');
  798. console.log(' - caseId:', caseId || '无');
  799. console.log(' - activityId:', activityId || '无');
  800. console.log(' - planId:', planId || '无');
  801. console.log(' - schemeId:', schemeId || '无');
  802. console.log(' - finalPlanId (最终使用):', finalPlanId || '无');
  803. console.log(' - shareUserId:', shareUserId || '无');
  804. console.log(' - q (扫码链接):', q || '无');
  805. // 处理活动海报二维码(activityId作为qrCode参数值)
  806. if (activityId && !storeId) {
  807. console.log('🎯 ===========================================');
  808. console.log('🎯 [活动二维码] 检测到活动海报二维码');
  809. console.log('🎯 活动ID (activityId):', activityId);
  810. console.log('🎯 来源类型: 活动海报');
  811. console.log('🎯 ===========================================');
  812. // 活动二维码需要跳转到活动页面,不需要跳转到店铺
  813. wx.setStorageSync('scan_activityId', activityId);
  814. wx.setStorageSync('need_activity_redirect', true);
  815. return;
  816. }
  817. // 如果存在 storeId,说明是扫码进入具体门店(包括异业分享)
  818. if (storeId) {
  819. console.log('🏪 ===========================================');
  820. console.log('🏪 [店铺扫码] 检测到扫码参数');
  821. console.log('🏪 店铺ID (storeId):', storeId);
  822. console.log('🏪 扫码次数 (scanCount):', scanCount || '0');
  823. // 确定来源类型
  824. // 规则:
  825. // 1. 扫描异业码(partnerId)→ sourceType = "partner"
  826. // 2. 扫描员工码(employeeId、老板/员工)→ sourceType = "sales" 或 "promoter"
  827. // 3. 扫描其他码(产品码、案例码等)→ sourceType = "scan"
  828. let sourceType = 'scan'; // 默认为通用扫码
  829. let sourceId = null;
  830. if (partnerId) {
  831. // 异业合作伙伴二维码
  832. sourceType = 'partner';
  833. sourceId = partnerId;
  834. console.log('🤝 [来源识别] 来源类型: 异业合作伙伴 (partner)');
  835. console.log('🤝 合作伙伴ID (partnerId):', partnerId);
  836. } else if (employeeId) {
  837. // 员工二维码(包括老板和员工)
  838. sourceType = 'sales';
  839. sourceId = employeeId;
  840. console.log('👤 [来源识别] 来源类型: 员工展业 (sales)');
  841. console.log('👤 员工ID (employeeId):', employeeId);
  842. } else if (ownerId) {
  843. // 老板二维码
  844. sourceType = 'sales';
  845. sourceId = ownerId;
  846. console.log('👔 [来源识别] 来源类型: 老板展业 (sales)');
  847. console.log('👔 老板ID (ownerId):', ownerId);
  848. } else if (userId) {
  849. // 推广员二维码
  850. sourceType = 'promoter';
  851. sourceId = userId;
  852. console.log('📢 [来源识别] 来源类型: 推广员展业 (promoter)');
  853. console.log('📢 推广员ID (userId):', userId);
  854. } else {
  855. // 其他情况(产品码、案例码、店铺分享等)
  856. sourceType = 'scan';
  857. console.log('🏬 [来源识别] 来源类型: 通用扫码 (scan)');
  858. }
  859. if (productId) {
  860. console.log('📦 [目标内容] 产品ID (productId):', productId);
  861. }
  862. if (caseId) {
  863. console.log('📸 [目标内容] 案例ID (caseId):', caseId);
  864. }
  865. if (finalPlanId) {
  866. console.log('📋 [目标内容] 方案ID (planId/schemeId):', finalPlanId);
  867. console.log('👤 [分享人] 分享用户ID (shareUserId):', shareUserId || '无');
  868. }
  869. console.log('🏪 ===========================================');
  870. // 保存到临时存储,用于后续跳转和统计
  871. console.log('💾 [保存扫码信息] 开始保存到本地存储...');
  872. wx.setStorageSync('scan_storeId', storeId);
  873. wx.setStorageSync('scan_scanCount', scanCount || '0');
  874. wx.setStorageSync('scan_sourceType', sourceType);
  875. wx.setStorageSync('scan_sourceId', sourceId || '');
  876. wx.setStorageSync('scan_ownerId', ownerId || '');
  877. wx.setStorageSync('scan_employeeId', employeeId || '');
  878. wx.setStorageSync('scan_partnerId', partnerId || '');
  879. wx.setStorageSync('scan_userId', userId || '');
  880. if (productId) {
  881. wx.setStorageSync('scan_productId', productId);
  882. }
  883. if (caseId) {
  884. wx.setStorageSync('scan_caseId', caseId);
  885. }
  886. if (finalPlanId) {
  887. wx.setStorageSync('scan_planId', finalPlanId);
  888. if (shareUserId) {
  889. wx.setStorageSync('scan_shareUserId', shareUserId);
  890. }
  891. }
  892. wx.setStorageSync('need_scan_redirect', true);
  893. console.log('✅ [保存完成] 扫码信息已保存');
  894. console.log('📋 [保存内容摘要]:');
  895. console.log(' - 来源类型:', sourceType);
  896. console.log(' - 来源ID:', sourceId || '无');
  897. console.log(' - 店铺ID:', storeId);
  898. console.log(' - 需要跳转:', true);
  899. } else if (partnerId) {
  900. // 理论上异业二维码现在也必须带 storeId,这里仅作为兜底保护
  901. console.warn('⚠️ 检测到异业合作伙伴二维码缺少 storeId,无法确定门店,请检查二维码生成逻辑');
  902. }
  903. },
  904. /**
  905. * 判断是否需要跳转到扫码统计页面
  906. */
  907. shouldRedirectToScanPage() {
  908. return wx.getStorageSync('need_scan_redirect') === true;
  909. },
  910. /**
  911. * 跳转到扫码对应的店铺页面
  912. * 根据 storeId 跳转到对应店铺,同时记录所有来源信息用于统计
  913. */
  914. async redirectToScanPage() {
  915. try {
  916. console.log('🚀 ===========================================');
  917. console.log('🚀 [跳转处理] 开始处理扫码跳转');
  918. console.log('🚀 ===========================================');
  919. // 获取所有扫码相关参数
  920. const storeId = wx.getStorageSync('scan_storeId');
  921. const scanCount = wx.getStorageSync('scan_scanCount');
  922. const sourceType = wx.getStorageSync('scan_sourceType');
  923. const sourceId = wx.getStorageSync('scan_sourceId');
  924. const ownerId = wx.getStorageSync('scan_ownerId');
  925. const employeeId = wx.getStorageSync('scan_employeeId');
  926. const partnerId = wx.getStorageSync('scan_partnerId');
  927. const userId = wx.getStorageSync('scan_userId');
  928. const productId = wx.getStorageSync('scan_productId');
  929. const caseId = wx.getStorageSync('scan_caseId');
  930. const planId = wx.getStorageSync('scan_planId');
  931. const shareUserId = wx.getStorageSync('scan_shareUserId');
  932. console.log('📖 [读取存储] 从本地存储读取扫码信息:');
  933. console.log(' - storeId:', storeId || '无');
  934. console.log(' - sourceType:', sourceType || '无');
  935. console.log(' - sourceId:', sourceId || '无');
  936. console.log(' - scanCount:', scanCount || '无');
  937. console.log(' - ownerId:', ownerId || '无');
  938. console.log(' - employeeId:', employeeId || '无');
  939. console.log(' - partnerId:', partnerId || '无');
  940. console.log(' - userId:', userId || '无');
  941. console.log(' - productId:', productId || '无');
  942. console.log(' - caseId:', caseId || '无');
  943. console.log(' - planId:', planId || '无');
  944. console.log(' - shareUserId:', shareUserId || '无');
  945. // 清除临时存储
  946. console.log('🧹 [清理存储] 清除临时扫码信息...');
  947. wx.removeStorageSync('scan_storeId');
  948. wx.removeStorageSync('scan_scanCount');
  949. wx.removeStorageSync('scan_sourceType');
  950. wx.removeStorageSync('scan_sourceId');
  951. wx.removeStorageSync('scan_ownerId');
  952. wx.removeStorageSync('scan_employeeId');
  953. wx.removeStorageSync('scan_partnerId');
  954. wx.removeStorageSync('scan_userId');
  955. wx.removeStorageSync('scan_productId');
  956. wx.removeStorageSync('scan_caseId');
  957. wx.removeStorageSync('scan_planId');
  958. wx.removeStorageSync('scan_shareUserId');
  959. wx.removeStorageSync('need_scan_redirect');
  960. console.log('✅ [清理完成] 临时存储已清除');
  961. if (!storeId) {
  962. console.error('❌ [错误] 缺少 storeId 参数,无法跳转');
  963. return;
  964. }
  965. console.log('📊 ===========================================');
  966. console.log('📊 [扫码来源汇总] 扫码进入店铺');
  967. console.log('📊 店铺 ID:', storeId);
  968. console.log('📊 来源类型:', sourceType);
  969. console.log('📊 来源 ID:', sourceId || '无');
  970. console.log('📊 扫码次数:', scanCount);
  971. if (ownerId) console.log('📊 老板 ID:', ownerId);
  972. if (employeeId) console.log('📊 员工 ID:', employeeId);
  973. if (partnerId) console.log('📊 合作伙伴 ID:', partnerId);
  974. if (userId) console.log('📊 推广员 ID:', userId);
  975. if (productId) console.log('📊 产品 ID:', productId);
  976. if (caseId) console.log('📊 案例 ID:', caseId);
  977. if (planId) {
  978. console.log('📊 方案 ID:', planId);
  979. console.log('📊 分享用户 ID:', shareUserId || '无');
  980. }
  981. console.log('📊 ===========================================');
  982. // 设置店铺 ID 到全局和本地存储
  983. wx.setStorageSync('storeId', storeId);
  984. getApp().globalData.storeId = storeId;
  985. // 保存来源信息到本地存储(用于后续统计或绑定关系)
  986. if (sourceId) {
  987. wx.setStorageSync('scan_from_sourceType', sourceType);
  988. wx.setStorageSync('scan_from_sourceId', sourceId);
  989. console.log('✅ 已记录来源信息:', sourceType, sourceId);
  990. }
  991. // 检查用户是否已登录
  992. const currentUser = Parse.User.current();
  993. console.log('👤 [登录检查] 检查用户登录状态...');
  994. console.log(' - 当前用户:', currentUser ? currentUser.id : '未登录');
  995. console.log(' - 手机号:', currentUser?.get('mobile') || '无');
  996. if (currentUser) {
  997. // 用户已登录,立即记录扫码统计
  998. console.log('✅ [已登录] 用户已登录,立即记录扫码统计');
  999. await this.recordScanStatistics({
  1000. storeId,
  1001. sourceType,
  1002. sourceId,
  1003. ownerId,
  1004. employeeId,
  1005. partnerId,
  1006. userId,
  1007. productId,
  1008. scanCount
  1009. });
  1010. } else {
  1011. // 用户未登录,保存扫码信息,等登录后再记录
  1012. console.log('⚠️ [未登录] 用户未登录,保存扫码信息待登录后记录');
  1013. const pendingData = {
  1014. storeId,
  1015. sourceType,
  1016. sourceId,
  1017. ownerId,
  1018. employeeId,
  1019. partnerId,
  1020. userId,
  1021. productId,
  1022. scanCount,
  1023. timestamp: Date.now()
  1024. };
  1025. console.log('💾 [待处理记录] 保存内容:', JSON.stringify(pendingData, null, 2));
  1026. wx.setStorageSync('pending_scan_record', pendingData);
  1027. console.log('✅ [保存成功] 待处理扫码记录已保存');
  1028. }
  1029. // 如果有产品ID,直接跳转到产品详情的 H5 页面
  1030. if (productId) {
  1031. console.log('🎯 检测到产品ID,跳转到产品详情页');
  1032. await this.redirectToProductDetail(storeId, productId, partnerId);
  1033. return;
  1034. }
  1035. // 如果有案例ID,直接跳转到案例详情的 H5 页面
  1036. if (caseId) {
  1037. console.log('🎯 检测到案例ID,跳转到案例详情页');
  1038. await this.redirectToCaseDetail(storeId, caseId, partnerId);
  1039. return;
  1040. }
  1041. // 如果有方案ID,跳转到门店首页并传递方案ID
  1042. if (planId) {
  1043. console.log('🎯 检测到方案ID,跳转到门店首页展示方案');
  1044. await this.redirectToStoreWithPlan(storeId, planId, shareUserId, partnerId);
  1045. return;
  1046. }
  1047. // 获取默认首页路径并跳转
  1048. let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath;
  1049. url += `?storeId=${storeId}`;
  1050. // 如果有产品ID,添加到URL参数中
  1051. if (productId) {
  1052. url += `&productId=${productId}`;
  1053. }
  1054. // 如果有异业合作伙伴ID,添加到URL参数中传回web-view
  1055. if (partnerId) {
  1056. url += `&partnerId=${partnerId}`;
  1057. }
  1058. console.log('✅ 跳转到店铺页面:', url);
  1059. wx.redirectTo({
  1060. url: url,
  1061. fail: (err) => {
  1062. console.error('❌ 跳转失败:', err);
  1063. // 如果 redirectTo 失败,尝试 reLaunch
  1064. wx.reLaunch({
  1065. url: url,
  1066. fail: (err2) => {
  1067. console.error('❌ reLaunch 也失败:', err2);
  1068. }
  1069. });
  1070. }
  1071. });
  1072. } catch (error) {
  1073. console.error('❌ 跳转到店铺页面失败:', error);
  1074. }
  1075. },
  1076. /**
  1077. * 跳转到活动页面
  1078. */
  1079. async redirectToActivityPage() {
  1080. try {
  1081. const activityId = wx.getStorageSync('scan_activityId');
  1082. // 清除临时存储
  1083. wx.removeStorageSync('scan_activityId');
  1084. wx.removeStorageSync('need_activity_redirect');
  1085. if (!activityId) {
  1086. console.error('❌ 缺少 activityId 参数,无法跳转');
  1087. return;
  1088. }
  1089. console.log('===========================================');
  1090. console.log('======= 扫码进入活动页面 =======');
  1091. console.log('活动 ID (activityId):', activityId);
  1092. console.log('===========================================');
  1093. // 保存活动ID
  1094. wx.setStorageSync('activityId', activityId);
  1095. // 获取默认首页路径并跳转(活动页面可能需要根据实际路由调整)
  1096. let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath;
  1097. url += `?activityId=${activityId}`;
  1098. console.log('✅ 跳转到活动页面:', url);
  1099. wx.redirectTo({
  1100. url: url,
  1101. fail: (err) => {
  1102. console.error('❌ 跳转失败:', err);
  1103. wx.reLaunch({
  1104. url: url,
  1105. fail: (err2) => {
  1106. console.error('❌ reLaunch 也失败:', err2);
  1107. }
  1108. });
  1109. }
  1110. });
  1111. } catch (error) {
  1112. console.error('❌ 跳转到活动页面失败:', error);
  1113. }
  1114. },
  1115. /**
  1116. * 跳转到产品详情的 H5 页面
  1117. * @param {string} storeId - 店铺 ID
  1118. * @param {string} productId - 产品 ID
  1119. * @param {string} partnerId - 可选的合作伙伴 ID
  1120. */
  1121. async redirectToProductDetail(storeId, productId, partnerId = null) {
  1122. try {
  1123. console.log('===========================================');
  1124. console.log('======= 跳转到产品详情页 =======');
  1125. console.log('店铺 ID:', storeId);
  1126. console.log('产品 ID:', productId);
  1127. if (partnerId) console.log('合作伙伴 ID:', partnerId);
  1128. console.log('===========================================');
  1129. const currentUser = Parse.User.current();
  1130. const token = currentUser ? currentUser.getSessionToken() : null;
  1131. // 构建产品详情的 H5 URL(不要在这里编码,后面统一编码)
  1132. let h5Url = `https://app.fmode.cn/dev/pobingfeng/owner/nav/products?storeId=${storeId}`;
  1133. // 如果有 token,添加到 URL
  1134. if (token) {
  1135. h5Url += `&token=${token}`;
  1136. } else {
  1137. // 如果没有 token,使用游客模式
  1138. h5Url += `&guestMode=true`;
  1139. }
  1140. // 添加产品ID(不要在这里编码,避免双重编码)
  1141. h5Url += `&productId=${productId}`;
  1142. // 如果有合作伙伴ID,也添加到URL中
  1143. if (partnerId) {
  1144. h5Url += `&partnerId=${partnerId}`;
  1145. }
  1146. console.log('🌐 H5 URL:', h5Url);
  1147. // 编码 URL(统一在这里编码一次)
  1148. const encodedUrl = encodeURIComponent(h5Url);
  1149. // 构建 web-view 页面路径
  1150. let webViewPath = `/common-page/pages/web-view/index?path=${encodedUrl}&storeId=${storeId}`;
  1151. console.log('📄 web-view 页面路径:', webViewPath.substring(0, 100) + '...');
  1152. console.log('===========================================');
  1153. wx.redirectTo({
  1154. url: webViewPath,
  1155. success: () => {
  1156. console.log('✅ 跳转到产品详情页成功');
  1157. },
  1158. fail: (err) => {
  1159. console.error('❌ 跳转失败:', err);
  1160. // 如果 redirectTo 失败,尝试 reLaunch
  1161. wx.reLaunch({
  1162. url: webViewPath,
  1163. fail: (err2) => {
  1164. console.error('❌ reLaunch 也失败:', err2);
  1165. }
  1166. });
  1167. }
  1168. });
  1169. } catch (error) {
  1170. console.error('❌ 跳转到产品详情页失败:', error);
  1171. }
  1172. },
  1173. /**
  1174. * 跳转到案例详情的 H5 页面
  1175. * @param {string} storeId - 店铺 ID
  1176. * @param {string} caseId - 案例 ID
  1177. * @param {string} partnerId - 可选的合作伙伴 ID
  1178. */
  1179. async redirectToCaseDetail(storeId, caseId, partnerId = null) {
  1180. try {
  1181. console.log('===========================================');
  1182. console.log('======= 跳转到案例详情页 =======');
  1183. console.log('店铺 ID:', storeId);
  1184. console.log('案例 ID:', caseId);
  1185. if (partnerId) console.log('合作伙伴 ID:', partnerId);
  1186. console.log('===========================================');
  1187. const currentUser = Parse.User.current();
  1188. const token = currentUser ? currentUser.getSessionToken() : null;
  1189. // 构建案例详情的 H5 URL(不要在这里编码,后面统一编码)
  1190. let h5Url = `https://app.fmode.cn/dev/pobingfeng/owner/nav/cases?storeId=${storeId}`;
  1191. // 如果有 token,添加到 URL
  1192. if (token) {
  1193. h5Url += `&token=${token}`;
  1194. } else {
  1195. // 如果没有 token,使用游客模式
  1196. h5Url += `&guestMode=true`;
  1197. }
  1198. // 添加案例ID(不要在这里编码,避免双重编码)
  1199. h5Url += `&caseId=${caseId}`;
  1200. // 如果有合作伙伴ID,也添加到URL中
  1201. if (partnerId) {
  1202. h5Url += `&partnerId=${partnerId}`;
  1203. }
  1204. console.log('🌐 H5 URL:', h5Url);
  1205. // 编码 URL(统一在这里编码一次)
  1206. const encodedUrl = encodeURIComponent(h5Url);
  1207. // 构建 web-view 页面路径
  1208. let webViewPath = `/common-page/pages/web-view/index?path=${encodedUrl}&storeId=${storeId}`;
  1209. console.log('📄 web-view 页面路径:', webViewPath.substring(0, 100) + '...');
  1210. console.log('===========================================');
  1211. wx.redirectTo({
  1212. url: webViewPath,
  1213. success: () => {
  1214. console.log('✅ 跳转到案例详情页成功');
  1215. },
  1216. fail: (err) => {
  1217. console.error('❌ 跳转失败:', err);
  1218. // 如果 redirectTo 失败,尝试 reLaunch
  1219. wx.reLaunch({
  1220. url: webViewPath,
  1221. fail: (err2) => {
  1222. console.error('❌ reLaunch 也失败:', err2);
  1223. }
  1224. });
  1225. }
  1226. });
  1227. } catch (error) {
  1228. console.error('❌ 跳转到案例详情页失败:', error);
  1229. }
  1230. },
  1231. /**
  1232. * 跳转到门店首页并展示方案
  1233. * @param {string} storeId - 店铺 ID
  1234. * @param {string} planId - 方案 ID
  1235. * @param {string} shareUserId - 分享方案的用户 ID
  1236. * @param {string} partnerId - 可选的合作伙伴 ID
  1237. */
  1238. async redirectToStoreWithPlan(storeId, planId, shareUserId = null, partnerId = null) {
  1239. try {
  1240. console.log('===========================================');
  1241. console.log('======= 跳转到门店首页展示方案 =======');
  1242. console.log('店铺 ID:', storeId);
  1243. console.log('方案 ID:', planId);
  1244. if (shareUserId) console.log('分享用户 ID:', shareUserId);
  1245. if (partnerId) console.log('合作伙伴 ID:', partnerId);
  1246. console.log('===========================================');
  1247. const currentUser = Parse.User.current();
  1248. const token = currentUser ? currentUser.getSessionToken() : null;
  1249. // 构建门店首页的 H5 URL,带上方案ID
  1250. let h5Url = `https://app.fmode.cn/dev/pobingfeng/owner/nav/home?storeId=${storeId}`;
  1251. // 如果有 token,添加到 URL
  1252. if (token) {
  1253. h5Url += `&token=${token}`;
  1254. } else {
  1255. // 如果没有 token,使用游客模式
  1256. h5Url += `&guestMode=true`;
  1257. }
  1258. // 添加方案ID(使用planId参数名)
  1259. h5Url += `&planId=${planId}`;
  1260. // 如果有分享用户ID,也添加到URL中
  1261. if (shareUserId) {
  1262. h5Url += `&shareUserId=${shareUserId}`;
  1263. }
  1264. // 如果有合作伙伴ID,也添加到URL中
  1265. if (partnerId) {
  1266. h5Url += `&partnerId=${partnerId}`;
  1267. }
  1268. console.log('🌐 H5 URL:', h5Url);
  1269. // 编码 URL
  1270. const encodedUrl = encodeURIComponent(h5Url);
  1271. // 构建 web-view 页面路径
  1272. let webViewPath = `/common-page/pages/web-view/index?path=${encodedUrl}&storeId=${storeId}`;
  1273. console.log('📄 web-view 页面路径:', webViewPath.substring(0, 100) + '...');
  1274. console.log('===========================================');
  1275. wx.redirectTo({
  1276. url: webViewPath,
  1277. success: () => {
  1278. console.log('✅ 跳转到门店首页展示方案成功');
  1279. },
  1280. fail: (err) => {
  1281. console.error('❌ 跳转失败:', err);
  1282. // 如果 redirectTo 失败,尝试 reLaunch
  1283. wx.reLaunch({
  1284. url: webViewPath,
  1285. fail: (err2) => {
  1286. console.error('❌ reLaunch 也失败:', err2);
  1287. }
  1288. });
  1289. }
  1290. });
  1291. } catch (error) {
  1292. console.error('❌ 跳转到门店首页展示方案失败:', error);
  1293. }
  1294. },
  1295. /**
  1296. * 记录扫码统计信息
  1297. * @param {Object} params - 扫码参数对象
  1298. * @param {string} params.storeId - 店铺 ID
  1299. * @param {string} params.sourceType - 来源类型 (employee/owner/partner/promoter/store)
  1300. * @param {string} params.sourceId - 来源 ID
  1301. * @param {string} params.ownerId - 老板 ID
  1302. * @param {string} params.employeeId - 员工 ID
  1303. * @param {string} params.partnerId - 合作伙伴 ID
  1304. * @param {string} params.userId - 推广员 ID
  1305. * @param {string} params.productId - 产品 ID
  1306. * @param {string} params.scanCount - 扫码次数
  1307. */
  1308. async recordScanStatistics(params) {
  1309. try {
  1310. console.log('📝 ===========================================');
  1311. console.log('📝 [扫码统计] 开始记录扫码统计');
  1312. console.log('📝 ===========================================');
  1313. const {
  1314. storeId,
  1315. sourceType,
  1316. sourceId,
  1317. ownerId,
  1318. employeeId,
  1319. partnerId,
  1320. userId,
  1321. productId,
  1322. scanCount
  1323. } = params;
  1324. console.log('📋 [统计参数] 接收到的参数:');
  1325. console.log(' - storeId:', storeId);
  1326. console.log(' - sourceType:', sourceType);
  1327. console.log(' - sourceId:', sourceId || '无');
  1328. console.log(' - ownerId:', ownerId || '无');
  1329. console.log(' - employeeId:', employeeId || '无');
  1330. console.log(' - partnerId:', partnerId || '无');
  1331. console.log(' - userId:', userId || '无');
  1332. console.log(' - productId:', productId || '无');
  1333. console.log(' - scanCount:', scanCount || '0');
  1334. // 检查用户是否已登录
  1335. const currentUser = Parse.User.current();
  1336. if (!currentUser) {
  1337. console.log('⚠️ [未登录] 用户未登录,不记录扫码统计');
  1338. return;
  1339. }
  1340. console.log('👤 [用户信息] 当前登录用户:');
  1341. console.log(' - 用户ID:', currentUser.id);
  1342. console.log(' - 手机号:', currentUser.get('mobile') || '无');
  1343. // 如果没有来源信息,不记录统计
  1344. if (!sourceId && !ownerId && !employeeId && !partnerId && !userId) {
  1345. console.log('ℹ️ [跳过] 无来源信息,跳过统计记录');
  1346. return;
  1347. }
  1348. console.log('✅ [开始记录] 用户已登录且有来源信息,开始记录');
  1349. // 创建扫码记录
  1350. const ScanRecord = Parse.Object.extend('ScanRecord');
  1351. const record = new ScanRecord();
  1352. record.set('company', {
  1353. __type: 'Pointer',
  1354. className: 'Company',
  1355. objectId: getApp().globalData.company
  1356. });
  1357. record.set('storeId', storeId);
  1358. record.set('sourceType', sourceType || 'unknown');
  1359. record.set('scanCount', parseInt(scanCount) || 0);
  1360. record.set('scanTime', new Date());
  1361. console.log('📦 [记录内容] 准备保存到 ScanRecord 表:');
  1362. console.log(' - company:', getApp().globalData.company);
  1363. console.log(' - storeId:', storeId);
  1364. console.log(' - sourceType:', sourceType || 'unknown');
  1365. console.log(' - scanCount:', parseInt(scanCount) || 0);
  1366. console.log(' - scanTime:', new Date().toISOString());
  1367. // 根据来源类型设置对应的ID
  1368. if (ownerId) {
  1369. record.set('ownerId', ownerId);
  1370. console.log(' - ownerId:', ownerId);
  1371. }
  1372. if (employeeId) {
  1373. record.set('employeeId', employeeId);
  1374. console.log(' - employeeId:', employeeId);
  1375. }
  1376. if (partnerId) {
  1377. record.set('partnerId', partnerId);
  1378. console.log(' - partnerId:', partnerId);
  1379. }
  1380. if (userId) {
  1381. record.set('userId', userId);
  1382. console.log(' - userId:', userId);
  1383. }
  1384. if (productId) {
  1385. record.set('productId', productId);
  1386. console.log(' - productId:', productId);
  1387. }
  1388. // 记录扫码用户
  1389. record.set('user', {
  1390. __type: 'Pointer',
  1391. className: '_User',
  1392. objectId: currentUser.id
  1393. });
  1394. console.log(' - user:', currentUser.id);
  1395. await record.save();
  1396. console.log('✅ [保存成功] 扫码记录已保存到数据库');
  1397. console.log(' - 记录ID:', record.id);
  1398. // 如果存在异业合作伙伴ID,处理异业绑定和扫码次数
  1399. if (partnerId) {
  1400. try {
  1401. console.log('🤝 ===========================================');
  1402. console.log('🤝 [异业处理] 开始处理异业扫码逻辑');
  1403. console.log('🤝 用户ID:', currentUser.id);
  1404. console.log('🤝 异业ID:', partnerId);
  1405. console.log('🤝 门店ID:', storeId);
  1406. console.log('🤝 ===========================================');
  1407. // 检查用户是否已经绑定了异业合作伙伴
  1408. const userBoundPartner = currentUser.get('Partner');
  1409. const userBoundPartnerId = userBoundPartner ? userBoundPartner.id : null;
  1410. console.log('🔍 [绑定检查] 用户已绑定的异业ID:', userBoundPartnerId || '无');
  1411. // 规则1:如果用户已绑定其他异业,不处理当前异业的扫码
  1412. if (userBoundPartnerId && userBoundPartnerId !== partnerId) {
  1413. console.log('⚠️ [规则1] 用户已绑定其他异业,不处理当前异业的扫码');
  1414. console.log(' - 已绑定异业:', userBoundPartnerId);
  1415. console.log(' - 当前扫码异业:', partnerId);
  1416. console.log('🤝 ===========================================');
  1417. return;
  1418. }
  1419. // 规则2:检查该用户在该门店是否已经扫过该异业的码
  1420. console.log('🔍 [重复检查] 检查是否已扫过该异业的码...');
  1421. const ScanRecord = Parse.Object.extend('ScanRecord');
  1422. const scanQuery = new Parse.Query(ScanRecord);
  1423. scanQuery.equalTo('user', {
  1424. __type: 'Pointer',
  1425. className: '_User',
  1426. objectId: currentUser.id
  1427. });
  1428. scanQuery.equalTo('partnerId', partnerId);
  1429. scanQuery.equalTo('storeId', storeId);
  1430. const existingScan = await scanQuery.first();
  1431. if (existingScan) {
  1432. console.log('⚠️ [规则2] 该用户在该门店已扫过该异业的码,不重复计数');
  1433. console.log(' - 首次扫码时间:', existingScan.get('scanTime'));
  1434. console.log(' - 记录ID:', existingScan.id);
  1435. console.log('🤝 ===========================================');
  1436. return;
  1437. }
  1438. console.log('✅ [首次扫码] 该用户在该门店首次扫该异业码');
  1439. // 规则3:如果用户还没有绑定异业,绑定当前异业
  1440. if (!userBoundPartnerId) {
  1441. console.log('🔗 [规则3] 用户首次扫异业码,开始绑定异业合作伙伴');
  1442. console.log(' - 异业ID:', partnerId);
  1443. currentUser.set('Partner', {
  1444. __type: 'Pointer',
  1445. className: 'Partner',
  1446. objectId: partnerId
  1447. });
  1448. await currentUser.save();
  1449. console.log('✅ [绑定成功] 异业绑定成功');
  1450. } else {
  1451. console.log('ℹ️ [已绑定] 用户已绑定该异业,无需重复绑定');
  1452. }
  1453. // 规则4:为该异业在该门店增加扫码次数
  1454. console.log('📊 [规则4] 开始更新异业扫码次数...');
  1455. const Partner = Parse.Object.extend('Partner');
  1456. const partnerQuery = new Parse.Query(Partner);
  1457. const partnerObj = await partnerQuery.get(partnerId);
  1458. if (partnerObj) {
  1459. console.log('📋 [异业信息] 找到异业合作伙伴记录');
  1460. console.log(' - 异业ID:', partnerObj.id);
  1461. console.log(' - 异业名称:', partnerObj.get('name') || '未设置');
  1462. // 获取或初始化门店扫码次数映射
  1463. let storeScans = partnerObj.get('storeScans') || {};
  1464. const oldCount = storeScans[storeId] || 0;
  1465. // 为该门店的扫码次数 +1
  1466. storeScans[storeId] = oldCount + 1;
  1467. partnerObj.set('storeScans', storeScans);
  1468. // 同时更新总扫码次数
  1469. const oldTotalCount = partnerObj.get('scanCount') || 0;
  1470. partnerObj.increment('scanCount', 1);
  1471. await partnerObj.save();
  1472. console.log('✅ [更新成功] 异业扫码次数已更新');
  1473. console.log(' - 门店ID:', storeId);
  1474. console.log(' - 该门店扫码次数: %d → %d', oldCount, storeScans[storeId]);
  1475. console.log(' - 总扫码次数: %d → %d', oldTotalCount, oldTotalCount + 1);
  1476. } else {
  1477. console.error('❌ [错误] 未找到异业合作伙伴记录');
  1478. }
  1479. console.log('🤝 ===========================================');
  1480. } catch (e) {
  1481. console.error('❌ [异业处理失败] 处理异业合作伙伴绑定失败:', e);
  1482. console.error(' - 错误信息:', e.message);
  1483. console.error(' - 错误堆栈:', e.stack);
  1484. console.log('🤝 ===========================================');
  1485. }
  1486. }
  1487. } catch (error) {
  1488. console.warn('⚠️ 保存扫码记录失败:', error);
  1489. // 不影响主流程
  1490. }
  1491. }
  1492. });