| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330 |
- // var Parse = getApp().Parse;
- // var app = getApp()
- // const { wxLogin } = require('./utils/login')
- const CONFIG = require("config.js");
- let config = {
- appid: CONFIG.default.appid,
- company: CONFIG.default.company,
- rootPage: CONFIG.default.rootPage,
- }
- const plugin = requirePlugin('fm-plugin')
- const { Parse, checkAuth } = plugin
- Page({
- /**
- * 页面的初始数据
- */
- data: {
- splashUrl: wx.getStorageSync("enabledOptions")[0],
- loading:true
- },
- /**
- * 生命周期函数--监听页面加载
- */
- onLoad: async function (options) {
- // 拦截插件的"登录信息已过期"提示
- const originalShowModal = wx.showModal;
- wx.showModal = function(options) {
- // 如果是"登录信息已过期"的提示,改为友好的重新登录提示
- if (options.content && options.content.includes('登录信息过期')) {
- console.log('⚠️ 拦截到"登录信息已过期"提示,改为友好提示');
-
- return originalShowModal.call(this, {
- title: '提示',
- content: '登录状态异常,是否重新登录?',
- showCancel: true,
- cancelText: '取消',
- confirmText: '重新登录',
- success: (result) => {
- if (result.confirm) {
- // 清除登录状态
- wx.removeStorageSync("sessionToken");
- wx.removeStorageSync("userLogin");
-
- // 重新加载首页
- const rootPage = getApp().globalData.rootPage || getApp().globalData.defaultTabBar?.list?.[0]?.pagePath || '/pages/index/index';
- wx.reLaunch({
- url: rootPage
- });
- }
-
- // 调用原始的 success 回调
- if (options.success) {
- options.success(result);
- }
- }
- });
- }
-
- // 其他提示正常显示
- return originalShowModal.call(this, options);
- };
-
- wx.login({
- success: function (res) {
- if (res.code) {
- console.log(res);
- // wx.request({
- // url: "https://server.fmode.cn/api/wxapp/auth_wxapp",
- // data: {
- // c: getApp().globalData.company,
- // code: res.code,
- // },
- // async success(res) {
- // wx.setStorageSync("userInfo", res.data);
- // resolve(res)
- // },
- // });
- }
- },
- fail: function (err) {
- wx.showToast({
- title: '服务器繁忙,请稍后重试',
- })
- }
- });
- wx.setStorageSync("invite", null);
-
- // 处理扫码链接(options.q 包含完整的URL或activityId)
- if (options.q) {
- let str = decodeURIComponent(options.q); // 扫描二维码获得的跳转链接
- // 兼容一些环境中把 & 编码成 & 的情况,防止参数解析错误(如 scanCount=0&storeId=...)
- if (str.indexOf('&') !== -1) {
- console.log('🔧 检测到 URL 中包含 &,自动还原为 &');
- str = str.replace(/&/g, '&');
- }
- try { wx.setStorageSync('scan_raw_url', str); } catch (e) {}
-
- // 检查是否是员工邀请链接(app.fmode.cn/dev/pobingfeng/manager/staff?invite=xxx)
- if (str.includes('app.fmode.cn/dev/pobingfeng/manager/staff')) {
- let obj = this.getParaName(str);
- if (obj && obj.invite) {
- wx.setStorageSync("invite", obj.invite);
- console.log('✅ 检测到员工邀请链接,invite:', obj.invite);
- }
- // 员工邀请链接不需要跳转到店铺,直接返回
- this.setData({ options: options });
- plugin.init(config, wx.getStorageSync('invite'));
- return;
- }
-
- // 检查是否是活动海报二维码(activityId作为qrCode参数值)
- // 如果str不包含http/https,且看起来像是一个ID,则可能是activityId
- if (!str.includes('http://') && !str.includes('https://') && !str.includes('?')) {
- // 可能是活动ID,尝试作为activityId处理
- if (str && str.length > 0 && !isNaN(str)) {
- options.activityId = str;
- console.log('✅ 检测到活动海报二维码,activityId:', str);
- // 活动二维码需要特殊处理,先保存activityId
- wx.setStorageSync("activityId", str);
- }
- }
-
- // 处理店铺相关的二维码链接(pwa.fmode.cn/gomini/pmd/)
- // 从完整URL中提取参数
- if (str.includes('pwa.fmode.cn/gomini/pmd') || str.includes('?')) {
- let obj = this.getParaName(str);
- if (obj && obj.invite) {
- wx.setStorageSync("invite", obj.invite);
- }
- // 将所有参数合并到 options 中
- obj && Object.keys(obj).forEach(key=> options[key] = obj[key]);
- }
- }
- // 兼容从编译参数或页面直达传入的 storeId
- if (options && options.scene && !options.storeId) {
- try {
- const sceneStr = decodeURIComponent(options.scene)
- if (sceneStr) {
- const pairs = sceneStr.split('&')
- for (const p of pairs) {
- const [k, v] = p.split('=')
- if (k === 'storeId' && v) {
- options.storeId = v
- break
- }
- }
- }
- } catch (e) {
- console.warn('解析 scene 失败:', e)
- }
- }
- this.setData({
- options: options
- })
- let {
- time,
- dramaId,
- roomId,
- orderId,
- shopId,
- invite,
- activityId,
- company,
- inviteHost,
- storeId
- } = options
- time && wx.setStorageSync("time", time);
- dramaId && wx.setStorageSync("dramaId", dramaId);
- roomId && wx.setStorageSync("roomId", roomId);
- orderId && wx.setStorageSync("orderId", orderId);
- shopId && wx.setStorageSync("shopId", shopId);
- invite && wx.setStorageSync("invite", invite);
- activityId && wx.setStorageSync("activityId", activityId);
- inviteHost && wx.setStorageSync("inviteHost", true);
- if (storeId) {
- // 扫码进入时,强制设置店铺 ID,标记为扫码来源
- wx.setStorageSync('storeId', storeId)
- getApp().globalData.storeId = storeId
- wx.setStorageSync('storeId_from_scan', true)
- console.log('✅ 入口页扫码进入,已设置店铺 ID(优先级最高):', storeId)
- }
- if (company) getApp().globalData.toCompany = true;
-
- // 检查是否是扫码进入(需要统计扫码次数)
- this.checkAndHandleScan(options);
-
- plugin.init(config, wx.getStorageSync('invite'))
-
- try {
- getApp().checkTrafficDeductPending = this.checkTrafficDeductPending.bind(this);
- console.log('🚦 [traffic] 已注册全局复判方法 checkTrafficDeductPending');
- } catch (e) {
- console.warn('🚦 [traffic] 注册全局复判方法失败:', e?.message || e);
- }
- try {
- getApp().checkAndRecordPendingScan = this.checkAndRecordPendingScan.bind(this);
- } catch (e) {}
- },
- /**
- * 生命周期函数--监听页面初次渲染完成
- */
- onReady: async function () { },
- /**
- * 生命周期函数--监听页面显示
- */
- onShow: async function () {
- await this.review()
- try {
- if (typeof this.checkTrafficDeductPending === 'function') {
- console.log('🚦 [traffic] onShow 触发暂缓扣减复判');
- await this.checkTrafficDeductPending();
- }
- } catch (e) {
- console.warn('🚦 [traffic] onShow 复判触发失败:', e?.message || e);
- }
- },
- async review(force){
- try {
- let options = this.data.options
- let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath
- if (options) {
- let objArr = Object.keys(options)
- if (objArr && objArr.length > 0) {
- let parms = '?'
- objArr.forEach((o, index) => {
- if (index > 0) {
- parms += '&' + o + '=' + options[o]
- } else {
- parms += o + '=' + options[o]
- }
- })
- url += parms
- }
- }
- let currentUser = Parse.User.current()
- console.log('===========================================');
- console.log('======= index.js review 方法 =======');
- console.log('当前用户:', currentUser ? currentUser.id : '无');
- console.log('用户手机号:', currentUser?.get('mobile') || '无');
- console.log('用户名:', currentUser?.get('username') || '无');
- console.log('Session Token:', currentUser?.getSessionToken()?.substring(0, 20) || '无');
- console.log('userLogin 存储:', wx.getStorageSync('userLogin') || '无');
- console.log('force 参数:', force);
- console.log('===========================================');
-
- // 查询 Company 的 isPublishing 字段
- let isPublishing = false;
- try {
- const companyQuery = new Parse.Query('Company');
- companyQuery.equalTo('objectId', getApp().globalData.company);
- companyQuery.select('isPublishing');
-
- try {
- const companyObj = await companyQuery.first();
-
- if (companyObj) {
- isPublishing = companyObj.get('isPublishing') === true;
- console.log('📋 Company isPublishing:', isPublishing);
- } else {
- console.log('⚠️ 未找到 Company 记录,默认 isPublishing = false');
- isPublishing = false;
- }
- } catch (queryError) {
- console.error('❌ 查询 Company 失败(可能 token 无效):', queryError.message);
- // 查询失败时,默认为 false(强制登录)
- isPublishing = false;
- }
-
- // 保存到全局,供其他页面使用
- getApp().globalData.isPublishing = isPublishing;
- wx.setStorageSync('isPublishing', isPublishing);
- } catch (error) {
- console.error('❌ 查询 Company isPublishing 失败:', error);
- // 查询失败时,默认为 false(强制登录)
- isPublishing = false;
- }
-
- // 根据 isPublishing 决定是否强制登录
- if (!currentUser || force) {
- console.log('🔄 开始调用 checkAuth...');
-
- // isPublishing == true 时不强制授权,否则强制授权
- const forceAuth = !isPublishing;
- console.log('🔐 是否强制登录:', forceAuth);
-
- let r = await checkAuth(forceAuth);
-
- console.log('===========================================');
- console.log('======= checkAuth 返回结果 =======');
- console.log('返回值:', r);
-
- // 重新获取用户信息
- currentUser = Parse.User.current();
- console.log('checkAuth 后的用户:', currentUser ? currentUser.id : '无');
- console.log('checkAuth 后的手机号:', currentUser?.get('mobile') || '无');
- console.log('checkAuth 后的 Session Token:', currentUser?.getSessionToken()?.substring(0, 20) || '无');
- console.log('===========================================');
-
- // 如果强制登录但用户未登录,不允许继续访问
- if (forceAuth && !r) {
- console.log('❌ 强制登录模式,用户未登录,停止访问');
- return;
- }
-
- // 即使登录失败,也允许继续访问(仅在非强制登录模式下)
- if(!r) {
- console.log('⚠️ 用户未登录或拒绝授权,允许游客访问');
- if (wx.getStorageSync('need_scan_redirect') === true) {
- await this.ensureTrackingUser();
- await this.checkAndRecordUserSourceOnLogin();
- }
- // 不要 return,继续执行后面的跳转逻辑
- } else {
- // 登录成功,设置 userLogin
- if (currentUser && currentUser.get('mobile')) {
- wx.setStorageSync("userLogin", currentUser.id);
- console.log('✅ 授权登录成功,已设置 userLogin:', currentUser.id);
- console.log('✅ 用户手机号:', currentUser.get('mobile'));
- } else {
- console.warn('⚠️ checkAuth 返回成功,但用户没有手机号!');
- console.warn(' 用户对象:', currentUser);
- }
-
- // 检查是否有待记录的扫码信息
- await this.checkAndRecordPendingScan();
- await this.checkTrafficDeductPending();
-
- // 如果用户没有来源信息,且当前有扫码参数,记录来源
- await this.checkAndRecordUserSourceOnLogin();
- }
- } else {
- console.log('✅ 用户已登录,跳过 checkAuth');
-
- // 用户已登录,确保 userLogin 已设置
- if (currentUser.get('mobile')) {
- wx.setStorageSync("userLogin", currentUser.id);
- console.log('✅ 已确认 userLogin:', currentUser.id);
- }
- this.updateUser(currentUser.id);
-
- // 用户已登录,检查是否有待记录的扫码信息
- await this.checkAndRecordPendingScan();
- await this.checkTrafficDeductPending();
-
- // 如果用户没有来源信息,且当前有扫码参数,记录来源
- await this.checkAndRecordUserSourceOnLogin();
- }
- getApp().Parse = Parse
- getApp().checkAuth = checkAuth
- getApp().checkTrafficDeductPending = this.checkTrafficDeductPending
- if (!await this.getCompanyServerExpire(url)) {
- return
- }
-
- // 检查是否需要跳转到活动页面
- if (wx.getStorageSync('need_activity_redirect') === true) {
- await this.redirectToActivityPage();
- return;
- }
-
- // 检查是否需要跳转到扫码统计页面
- if (this.shouldRedirectToScanPage()) {
- await this.redirectToScanPage();
- return;
- }
-
- console.log('✅ 准备跳转到:', url);
-
- wx.redirectTo({
- url: url,
- success: () => {
- console.log('✅ redirectTo 跳转成功');
- },
- fail: (err) => {
- console.error('❌ redirectTo 失败:', err);
- console.log('⚠️ 尝试使用 reLaunch');
-
- // 降级:尝试使用 reLaunch
- wx.reLaunch({
- url: url,
- success: () => {
- console.log('✅ reLaunch 跳转成功');
- },
- fail: (err2) => {
- console.error('❌ reLaunch 也失败:', err2);
- // 显示错误提示
- this.setData({ loading: false });
- wx.showModal({
- title: '温馨提示',
- content: '页面加载失败,请检查网络后重试。',
- showCancel: true,
- cancelText: '退出',
- confirmText: '重试',
- success: (result) => {
- if (result.confirm) {
- this.review(true);
- } else {
- wx.exitMiniProgram();
- }
- }
- });
- }
- });
- }
- });
- }
- catch (err) {
- console.log('❌ review 方法出错:', err);
-
- /* 登录身份信息到期,重新登陆 */
- if((err?.message?.indexOf('Session token is expired') != -1 || err?.message?.indexOf('Invalid session token') != -1) && !force){
- console.log('⚠️ Session Token 过期,准备重新登录');
-
- // 保存需要保留的数据
- const invite = wx.getStorageSync('invite');
- const agreementAccepted = wx.getStorageSync('user_agreement_accepted');
- const storeId = wx.getStorageSync('storeId');
- const isPublishing = wx.getStorageSync('isPublishing');
-
- // 先登出 Parse 用户
- try {
- await Parse.User.logOut();
- console.log('✅ Parse 用户已登出');
- } catch (logoutErr) {
- console.warn('⚠️ Parse 登出失败:', logoutErr);
- }
-
- // 清除所有存储
- wx.clearStorageSync();
-
- // 恢复需要保留的数据
- if (invite) wx.setStorageSync('invite', invite);
- if (agreementAccepted) wx.setStorageSync('user_agreement_accepted', agreementAccepted);
- if (storeId) wx.setStorageSync('storeId', storeId);
- if (isPublishing !== undefined) wx.setStorageSync('isPublishing', isPublishing);
-
- console.log('✅ 已清除过期登录信息,保留必要数据');
- console.log(' 保留的 invite:', invite || '无');
- console.log(' 保留的协议状态:', agreementAccepted || '无');
- console.log(' 保留的店铺ID:', storeId || '无');
-
- /* 强制重新登录 */
- this.review(true);
- return;
- }
-
- // 如果出错,尝试继续跳转到主页(游客模式)
- console.log('⚠️ 登录出错,尝试游客模式访问');
-
- let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath;
- if (this.data.options) {
- let objArr = Object.keys(this.data.options)
- if (objArr && objArr.length > 0) {
- let parms = '?'
- objArr.forEach((o, index) => {
- if (index > 0) {
- parms += '&' + o + '=' + this.data.options[o]
- } else {
- parms += o + '=' + this.data.options[o]
- }
- })
- url += parms
- }
- }
-
- wx.redirectTo({
- url: url,
- fail: () => {
- // 如果跳转失败,显示错误提示
- this.setData({
- loading:false
- })
- wx.showModal({
- title: '温馨提示',
- content: '页面加载失败,请检查网络后重试。',
- showCancel: true,
- cancelText: '退出',
- confirmText: '重试',
- success: (result) => {
- if (result.confirm) {
- // 用户选择重试
- this.review(true)
- } else {
- // 用户选择退出
- wx.exitMiniProgram()
- }
- },
- });
- }
- });
- }
- },
-
- /**
- * 检查并记录待处理的扫码统计
- * 同时记录用户来源信息到 _User 表
- */
- async checkAndRecordPendingScan() {
- try {
- const pendingScan = wx.getStorageSync('pending_scan_record');
-
- if (!pendingScan) {
- return;
- }
-
- console.log('===========================================');
- console.log('======= 发现待记录的扫码信息 =======');
- console.log('扫码信息:', pendingScan);
- console.log('===========================================');
-
- // 检查是否超时(24小时)
- const now = Date.now();
- const scanTime = pendingScan.timestamp || 0;
- const hoursPassed = (now - scanTime) / (1000 * 60 * 60);
-
- if (hoursPassed > 24) {
- console.log('⚠️ 扫码信息已超过24小时,不再记录');
- wx.removeStorageSync('pending_scan_record');
- return;
- }
-
- // 记录用户来源信息
- await this.recordUserSource(pendingScan);
-
- // 记录扫码统计
- await this.recordScanStatistics({
- storeId: pendingScan.storeId,
- sourceType: pendingScan.sourceType,
- sourceId: pendingScan.sourceId,
- ownerId: pendingScan.ownerId,
- employeeId: pendingScan.employeeId,
- partnerId: pendingScan.partnerId,
- userId: pendingScan.userId,
- productId: pendingScan.productId,
- scanCount: pendingScan.scanCount,
- scanTimestamp: pendingScan.timestamp
- });
-
- // 清除待记录的扫码信息
- wx.removeStorageSync('pending_scan_record');
- console.log('✅ 扫码统计已记录并清除');
-
- } catch (error) {
- console.error('❌ 记录待处理扫码信息失败:', error);
- }
- },
- /**
- * 检查并处理暂缓扣减任务(在获取到手机号后复判白名单并决定是否扣减)
- */
- async checkTrafficDeductPending() {
- try {
- const pending = wx.getStorageSync('traffic_deduct_pending');
- if (!pending) return;
- const currentUser = Parse.User.current();
- if (!currentUser || !currentUser.id) return;
- const beforeNumbers = collectUserMobiles(currentUser);
- console.log('🚦 [traffic] 检测到暂缓扣减任务:', {
- storeId: pending && pending.storeId,
- userId: currentUser.id,
- userNumbers: beforeNumbers
- });
- let userNumbers = beforeNumbers;
- try {
- const sessionToken = typeof currentUser.getSessionToken === 'function' ? currentUser.getSessionToken() : null;
- const q = new Parse.Query('_User');
- q.select('mobile', 'phone');
- const freshUser = await q.get(currentUser.id, sessionToken ? { sessionToken } : undefined);
- const afterNumbers = collectUserMobiles(freshUser);
- console.log('🚦 [traffic] 刷新用户后号码:', { before: beforeNumbers, after: afterNumbers });
- if (Array.isArray(afterNumbers) && afterNumbers.length) {
- userNumbers = afterNumbers;
- }
- } catch (e) {
- console.warn('🚦 [traffic] 刷新用户手机号失败:', e?.message || e);
- }
- if (!userNumbers || !userNumbers.length) {
- const storageMobile = wx.getStorageSync('user_mobile') || '';
- console.log('🚦 [traffic] 仍未获取到手机号,继续暂缓,并安排 800ms 后重试', {
- storageMobile
- });
- try {
- const retryInfo = wx.getStorageSync('traffic_deduct_retry') || { count: 0 };
- if ((retryInfo.count || 0) < 3) {
- wx.setStorageSync('traffic_deduct_retry', { count: (retryInfo.count || 0) + 1 });
- setTimeout(() => {
- try {
- if (typeof getApp().checkTrafficDeductPending === 'function') {
- console.log('🚦 [traffic] 重试触发暂缓扣减复判');
- getApp().checkTrafficDeductPending();
- }
- } catch (e) {}
- }, 800);
- }
- } catch (e) {}
- return;
- }
- const storeId = pending && pending.storeId ? pending.storeId : null;
- if (!storeId) {
- console.warn('🚦 [traffic] 暂缓扣减任务缺少 storeId,清除任务');
- wx.removeStorageSync('traffic_deduct_pending');
- return;
- }
- const isExempt = await isTrafficExemptForStore(Parse, storeId, currentUser);
- if (isExempt) {
- console.log('🚦 [traffic] 暂缓扣减复判:命中白名单,取消扣减并清除暂缓任务');
- try {
- currentUser.set('trafficExempt', true);
- currentUser.set('trafficExemptAt', new Date());
- currentUser.set('trafficExemptStoreId', storeId);
- await currentUser.save();
- } catch (e) {
- console.warn('🚦 [traffic] 写入 trafficExempt 失败(不影响继续访问):', e?.message || e);
- }
- wx.removeStorageSync('traffic_deduct_pending');
- return;
- }
- console.log('🚦 [traffic] 暂缓扣减复判:未命中白名单,执行扣减');
- try {
- await decrementStoreTrafficImpl(Parse, storeId);
- const deductedStores = Array.isArray(currentUser.get('trafficDeductedStores')) ? currentUser.get('trafficDeductedStores') : [];
- const updatedStores = Array.isArray(deductedStores) ? deductedStores.slice() : [];
- if (!updatedStores.includes(storeId)) updatedStores.push(storeId);
- currentUser.set('trafficDeductedStores', updatedStores);
- currentUser.set('trafficDeductedStoreId', storeId);
- const atMap = currentUser.get('trafficDeductedAtMap') || {};
- atMap[storeId] = new Date();
- currentUser.set('trafficDeductedAtMap', atMap);
- await currentUser.save();
- console.log('🚦✅ [traffic] 暂缓扣减复判:已为店铺扣减 1 个流量,storeId:', storeId);
- } catch (decErr) {
- console.error('🚦❌ [traffic] 暂缓扣减复判:扣减失败:', decErr?.message || decErr);
- }
- wx.removeStorageSync('traffic_deduct_pending');
- } catch (error) {
- console.error('🚦❌ [traffic] 处理暂缓扣减任务失败:', error);
- }
- },
- async ensureTrackingUser() {
- try {
- let currentUser = Parse.User.current();
- if (currentUser) {
- return currentUser;
- }
- if (Parse.AnonymousUtils && typeof Parse.AnonymousUtils.logIn === 'function') {
- await Parse.AnonymousUtils.logIn();
- currentUser = Parse.User.current();
- return currentUser || null;
- }
- return null;
- } catch (e) {
- console.warn('ensureTrackingUser 失败:', e?.message || e);
- return null;
- }
- },
-
- /**
- * 记录用户来源信息到 _User 表
- * 根据扫码参数判断来源类型并保存到用户的 source 字段
- *
- * 来源类型:
- * 1. 渠道xxx→异业xxx(老板后台添加的异业)
- * 2. 渠道xxx→异业xxx→员工xxx(员工后台添加的异业)
- * 3. 员工xxx
- * 4. 老板
- * 5. 自主进入(无任何推荐)
- */
- async recordUserSource(scanInfo) {
- try {
- const currentUser = Parse.User.current();
- if (!currentUser) {
- console.log('⚠️ 用户未登录,无法记录来源');
- return;
- }
-
- const existingSource = currentUser.get('source');
-
- console.log('📊 ===========================================');
- console.log('📊 [记录来源] 开始记录用户来源信息');
- console.log('📊 用户ID:', currentUser.id);
- console.log('📊 ===========================================');
-
- const { partnerId, employeeId, ownerId, userId, storeId } = scanInfo;
- console.log('📌 [扣减前置] storeId:', storeId || '无');
- console.log('📌 [扣减前置] existingSource:', existingSource ? '有' : '无');
- console.log('📌 [扣减前置] trafficDeducted:', currentUser.get('trafficDeducted') === true ? 'true' : 'false');
- console.log('📌 [扣减前置] trafficDeductedStoreId:', currentUser.get('trafficDeductedStoreId') || '无');
- console.log('📌 [扣减前置] trafficDeductedAt:', currentUser.get('trafficDeductedAt') || '无');
- try {
- const scanSnapshot = {
- storeId: wx.getStorageSync('scan_storeId') || null,
- sourceType: wx.getStorageSync('scan_sourceType') || null,
- sourceId: wx.getStorageSync('scan_sourceId') || null,
- ownerId: wx.getStorageSync('scan_ownerId') || null,
- employeeId: wx.getStorageSync('scan_employeeId') || null,
- partnerId: wx.getStorageSync('scan_partnerId') || null,
- userId: wx.getStorageSync('scan_userId') || null,
- productId: wx.getStorageSync('scan_productId') || null,
- caseId: wx.getStorageSync('scan_caseId') || null,
- planId: wx.getStorageSync('scan_planId') || null,
- shareUserId: wx.getStorageSync('scan_shareUserId') || null,
- scanCount: wx.getStorageSync('scan_scanCount') || null,
- rawUrl: wx.getStorageSync('scan_raw_url') || null
- };
- console.log('🔗 [扫码参数快照] scanInfo:', scanInfo);
- console.log('🔗 [扫码参数快照] storage:', scanSnapshot);
- } catch (e) {
- console.warn('⚠️ 读取扫码参数快照失败:', e?.message || e);
- }
-
- // 员工推荐判定与日志
- try {
- let staffJudge = { isStaff: false, reason: 'no_staff_params' };
- if (employeeId) {
- staffJudge = { isStaff: true, reason: 'employeeId_param', staffId: employeeId };
- } else if (ownerId) {
- staffJudge = { isStaff: true, reason: 'ownerId_param', staffId: ownerId };
- } else if (userId) {
- try {
- const uq = new Parse.Query('_User');
- const u = await uq.get(userId);
- const roles = Array.isArray(u.get('roles')) ? u.get('roles').map(r => String(r).toLowerCase()) : [];
- const isStaff = roles.includes('staff') || roles.includes('userstaff') || roles.includes('employee') || roles.includes('sales');
- staffJudge = { isStaff, reason: isStaff ? 'user_role_staff' : 'user_role_non_staff', staffId: userId, roles };
- } catch (e) {
- staffJudge = { isStaff: false, reason: 'user_lookup_failed', error: e?.message || e };
- }
- }
- console.log('👥 [员工推荐判定] 结果:', staffJudge);
- } catch (e) {
- console.warn('⚠️ [员工推荐判定] 失败:', e?.message || e);
- }
-
- // 来源优先级判定与升级覆盖
- const pickType = () => {
- if (partnerId && employeeId) return 'channel_partner_employee';
- if (partnerId) return 'channel_partner';
- if (employeeId) return 'employee';
- if (ownerId) return 'owner';
- if (userId) return 'promoter';
- return 'self_entry';
- };
- const prio = (t) => {
- if (t === 'channel_partner_employee') return 4;
- if (t === 'owner' || t === 'employee') return 3;
- if (t === 'channel_partner') return 2;
- if (t === 'promoter') return 1;
- return 0;
- };
- const newType = pickType();
- const oldType = existingSource && existingSource.type ? String(existingSource.type) : 'self_entry';
- const shouldOverride = prio(newType) > prio(oldType);
- console.log('🔀 [来源优先级] oldType:', oldType, 'newType:', newType, 'override:', shouldOverride ? 'YES' : 'NO');
- if (!existingSource || shouldOverride) {
- if (existingSource && shouldOverride) {
- console.log('🔁 [来源升级] 触发覆盖: 从', oldType, '→', newType);
- }
- let sourceInfo = {
- type: 'self_entry',
- label: '自主进入',
- timestamp: new Date(),
- storeId: storeId
- };
-
- if (partnerId && employeeId) {
- console.log('🔍 [来源类型] 渠道→异业→员工');
-
- try {
- const partnerQuery = new Parse.Query('Partner');
- const partner = await partnerQuery.get(partnerId);
- const partnerName = partner.get('name') || '未知异业';
- const channelName = partner.get('channelName') || partner.get('category') || '未知渠道';
-
- const employeeQuery = new Parse.Query('Employee');
- const employee = await employeeQuery.get(employeeId);
- const employeeName = employee.get('name') || '未知员工';
-
- sourceInfo = {
- type: 'channel_partner_employee',
- label: `${channelName},${partnerName},员工${employeeName}`,
- channelName: channelName,
- partnerId: partnerId,
- partnerName: partnerName,
- employeeId: employeeId,
- employeeName: employeeName,
- timestamp: new Date(),
- storeId: storeId
- };
-
- console.log('✅ [来源信息] 渠道→异业→员工:', sourceInfo.label);
- } catch (error) {
- console.error('❌ 查询异业或员工信息失败:', error);
- }
-
- } else if (partnerId && !employeeId) {
- console.log('🔍 [来源类型] 渠道→异业');
-
- try {
- const partnerQuery = new Parse.Query('Partner');
- const partner = await partnerQuery.get(partnerId);
- const partnerName = partner.get('name') || '未知异业';
- const channelName = partner.get('channelName') || partner.get('category') || '未知渠道';
-
- sourceInfo = {
- type: 'channel_partner',
- label: `${channelName},${partnerName}`,
- channelName: channelName,
- partnerId: partnerId,
- partnerName: partnerName,
- timestamp: new Date(),
- storeId: storeId
- };
-
- console.log('✅ [来源信息] 渠道→异业:', sourceInfo.label);
- } catch (error) {
- console.error('❌ 查询异业信息失败:', error);
- }
-
- } else if (employeeId && !partnerId) {
- console.log('🔍 [来源类型] 员工');
-
- try {
- // 兼容管理员端员工表 UserStaff
- let employeeName = '未知员工';
- try {
- const staffQuery = new Parse.Query('UserStaff');
- const staff = await staffQuery.get(employeeId);
- employeeName = staff.get('name') || staff.get('nickname') || staff.get('mobile') || '管理员端员工';
- } catch (e1) {
- try {
- const employeeQuery = new Parse.Query('Employee');
- const employee = await employeeQuery.get(employeeId);
- employeeName = employee.get('name') || '未知员工';
- } catch (e2) {}
- }
-
- sourceInfo = {
- type: 'employee',
- label: employeeName,
- employeeId: employeeId,
- employeeName: employeeName,
- timestamp: new Date(),
- storeId: storeId
- };
-
- console.log('✅ [来源信息] 员工:', sourceInfo.label);
- } catch (error) {
- console.error('❌ 查询员工信息失败:', error);
- }
-
- } else if (ownerId) {
- console.log('🔍 [来源类型] 老板');
-
- sourceInfo = {
- type: 'owner',
- label: '老板',
- ownerId: ownerId,
- timestamp: new Date(),
- storeId: storeId
- };
-
- console.log('✅ [来源信息] 老板');
-
- } else if (userId) {
- console.log('🔍 [来源类型] 推广员');
-
- try {
- const userQuery = new Parse.Query('_User');
- const promoter = await userQuery.get(userId);
- const promoterName = promoter.get('username') || promoter.get('mobile') || '未知推广员';
-
- sourceInfo = {
- type: 'promoter',
- label: `推广员${promoterName}`,
- userId: userId,
- promoterName: promoterName,
- timestamp: new Date(),
- storeId: storeId
- };
-
- console.log('✅ [来源信息] 推广员:', sourceInfo.label);
- } catch (error) {
- console.error('❌ 查询推广员信息失败:', error);
- }
-
- } else {
- console.log('🔍 [来源类型] 自主进入');
- console.log('✅ [来源信息] 自主进入');
- }
-
- currentUser.set('source', sourceInfo);
- await currentUser.save();
-
- console.log('✅ [保存成功] 用户来源信息已保存');
- console.log(' - 来源类型:', sourceInfo.type);
- console.log(' - 来源标签:', sourceInfo.label);
- console.log('📊 ===========================================');
- } else {
- console.log('ℹ️ 用户已有来源信息,跳过覆盖:', existingSource);
- }
-
- const deductedStores = Array.isArray(currentUser.get('trafficDeductedStores')) ? currentUser.get('trafficDeductedStores') : [];
- const hasDeductedForThisStore =
- (Array.isArray(deductedStores) && deductedStores.includes(storeId)) ||
- (currentUser.get('trafficDeductedStoreId') && currentUser.get('trafficDeductedStoreId') === storeId);
-
- if (storeId && !hasDeductedForThisStore) {
- const internal = await isInternalVisit(Parse, storeId, Parse.User.current(), { ownerId, employeeId, partnerId });
- if (internal) {
- console.log('ℹ️ [扣减跳过] 内部角色访问(老板/异业/员工),不扣减流量');
- } else {
- const nums = collectUserMobiles(currentUser);
- if (!nums || !nums.length) {
- console.log('ℹ️ [扣减暂停] 用户未提供手机号,暂缓扣减', { storeId, userId: currentUser.id });
- try {
- wx.setStorageSync('traffic_deduct_pending', { storeId, userId: currentUser.id, time: Date.now() });
- } catch (e) {}
- } else {
- const isExempt = await isTrafficExemptForStore(Parse, storeId, currentUser);
- if (isExempt) {
- console.log('ℹ️ [扣减跳过] 命中白名单手机号,跳过扣减流量');
- try {
- currentUser.set('trafficExempt', true);
- currentUser.set('trafficExemptAt', new Date());
- currentUser.set('trafficExemptStoreId', storeId);
- await currentUser.save();
- } catch (e) {
- console.warn('⚠️ [扣减跳过] 写入 trafficExempt 失败(不影响继续访问):', e?.message || e);
- }
- } else {
- console.log('🧮 [扣减流量] 检测到新用户首次扣减,准备扣减门店流量');
- try {
- await decrementStoreTrafficImpl(Parse, storeId);
- const updatedStores = Array.isArray(deductedStores) ? deductedStores.slice() : [];
- if (!updatedStores.includes(storeId)) updatedStores.push(storeId);
- currentUser.set('trafficDeductedStores', updatedStores);
- currentUser.set('trafficDeductedStoreId', storeId);
- const atMap = currentUser.get('trafficDeductedAtMap') || {};
- atMap[storeId] = new Date();
- currentUser.set('trafficDeductedAtMap', atMap);
- await currentUser.save();
- console.log('✅ [扣减成功] 已为店铺扣减 1 个流量,storeId:', storeId);
- } catch (decErr) {
- console.error('❌ [扣减失败] 扣减门店流量失败:', decErr?.message || decErr);
- }
- }
- }
- }
- } else if (!storeId) {
- console.warn('⚠️ [扣减跳过] storeId 为空,无法扣减 ShopStore.traffic');
- } else if (hasDeductedForThisStore) {
- console.log('ℹ️ [扣减跳过] 该用户已在该门店扣减过流量,不重复扣减');
- }
-
- } catch (error) {
- console.error('❌ 记录用户来源失败:', error);
- }
- },
-
- /**
- * 用户登录后检查并记录来源信息
- * 如果用户是首次登录且没有来源信息,根据当前的扫码参数记录来源
- */
- async checkAndRecordUserSourceOnLogin() {
- try {
- const currentUser = Parse.User.current();
- if (!currentUser) {
- return;
- }
-
- // 检查是否有扫码参数
- const scanStoreId = wx.getStorageSync('scan_storeId');
- const scanPartnerId = wx.getStorageSync('scan_partnerId');
- const scanEmployeeId = wx.getStorageSync('scan_employeeId');
- const scanOwnerId = wx.getStorageSync('scan_ownerId');
- const scanUserId = wx.getStorageSync('scan_userId');
-
- // 如果有任何扫码参数,记录来源
- if (scanStoreId || scanPartnerId || scanEmployeeId || scanOwnerId || scanUserId) {
- console.log('📊 检测到扫码参数,记录用户来源');
-
- await this.recordUserSource({
- storeId: scanStoreId,
- partnerId: scanPartnerId,
- employeeId: scanEmployeeId,
- ownerId: scanOwnerId,
- userId: scanUserId
- });
- } else {
- // 没有任何扫码参数,标记为自主进入
- console.log('📊 无扫码参数,标记为自主进入');
-
- await this.recordUserSource({
- storeId: wx.getStorageSync('storeId') || null
- });
- }
-
- } catch (error) {
- console.error('❌ 检查并记录用户来源失败:', error);
- }
- },
- async updateUser(id) {
- try {
- let User = new Parse.Query('_User')
- let user = await User.get(id)
- let invite = wx.getStorageSync('invite')
- //查询邀请人user
- let query = new Parse.Query("_User")
- query.equalTo('objectId', invite)
- let result = await query.first()
- if (result && result.id && result.get("invite")?.id == user.id) {
- console.error('邀请人不能是自己的下级')
- return
- }
- if (invite && !user.get('invite') && user.id != invite && !user.get('agentLevel')) {
- console.log('上下级绑定成功');
- user.set('invite', {
- __type: "Pointer",
- className: "_User",
- objectId: invite
- })
- user.set('agent', {
- __type: "Pointer",
- className: "_User",
- objectId: invite
- })
- await Parse.Cloud.run('user_save', {
- userJson: user.toJSON()
- })
- }
- } catch (error) {
- console.error('❌ updateUser 失败:', error.message);
- // 不阻断流程,继续执行
- }
- },
- async getCompanyServerExpire(url) {
- try {
- let query = new Parse.Query('Company')
- query.equalTo('objectId', getApp().globalData.company)
- query.select('expireDate', 'expireMap')
- let com = await query.first()
- if (com?.id && com?.get('expireDate')) {
- let now = + new Date()
- let expireTime = + new Date(com?.get('expireDate'))
- if (com?.get('expireMap') && com.get('expireMap')[getApp().globalData.appid]) {
- expireTime = + new Date(com.get('expireMap')[getApp().globalData.appid])
- }
- if (now >= expireTime) {
- console.log('服务器到期');
- wx.reLaunch({
- url: `common-page/pages/loading/index?url=${url}`,
- });
- return
- }
- }
- return true
- } catch (error) {
- console.error('❌ getCompanyServerExpire 失败:', error.message);
- // 查询失败时,允许继续访问
- return true
- }
- },
- onUnload: function () {
- wx.setStorageSync("active", 0);
- },
- getParaName(url) {
- if (!url || url.indexOf('?') == -1) {
- return
- }
- // 兼容 URL 中 & 的情况,先统一还原为 &
- if (url.indexOf('&') !== -1) {
- url = url.replace(/&/g, '&');
- }
- // 提取查询参数部分(去除可能的 hash 部分)
- let queryString = url.split('?')[1];
- // 如果包含 #,只取 # 之前的部分
- if (queryString.indexOf('#') !== -1) {
- queryString = queryString.split('#')[0];
- }
- return this.setObject(queryString) //封装成对象
- },
- setObject(paraArr) {
- let obj = {}
- let arr1 = paraArr.split('&')
- arr1.forEach(item => {
- let str = item.split('=')
- let key = str[0]
- let val = str[1]
- obj[key] = val
- })
- return obj
- },
- /**
- * 检查并处理扫码参数
- * 支持所有类型的二维码:
- * 1. 推广员二维码: ?scanCount=0&storeId=xxx&userId=xxx
- * 2. 产品二维码: ?scanCount=0&storeId=xxx&productId=xxx
- * 3. 案例二维码: ?scanCount=0&storeId=xxx&caseId=xxx
- * 4. 活动海报二维码: activityId作为qrCode参数值
- * 5. 异业合作伙伴二维码(移动端 / PC端统一): ?scanCount=xxx&storeId=xxx&partnerId=xxx
- * 6. 员工邀请二维码(移动端): ?scanCount=0&storeId=xxx&employeeId=xxx
- * 7. 我的二维码(老板): ?scanCount=0&storeId=xxx&ownerId=xxx
- * 8. 我的二维码(员工): ?scanCount=0&storeId=xxx&employeeId=xxx
- * 9. 方案分享二维码: ?scanCount=0&storeId=xxx&planId=xxx&shareUserId=xxx
- */
- checkAndHandleScan(options) {
- console.log('🔍🔍🔍 ========================================');
- console.log('🔍 [扫码检测] 开始检查扫码参数');
- console.log('🔍 完整 options 对象:', JSON.stringify(options, null, 2));
- console.log('🔍🔍🔍 ========================================');
-
- const {
- scanCount,
- ownerId,
- employeeId,
- partnerId,
- storeId,
- productId,
- caseId,
- userId, // 推广员二维码使用userId
- activityId,
- schemeId, // 方案ID(旧参数名)
- planId, // 方案ID(新参数名,优先使用)
- shareUserId, // 分享方案的用户ID
- q // 扫码链接
- } = options;
-
- // planId 和 schemeId 兼容处理,优先使用 planId
- const finalPlanId = planId || schemeId;
-
- console.log('📋 [扫码参数] 提取的参数:');
- console.log(' - scanCount:', scanCount || '无');
- console.log(' - storeId:', storeId || '无');
- console.log(' - ownerId:', ownerId || '无');
- console.log(' - employeeId:', employeeId || '无');
- console.log(' - partnerId:', partnerId || '无');
- console.log(' - userId:', userId || '无');
- console.log(' - productId:', productId || '无');
- console.log(' - caseId:', caseId || '无');
- console.log(' - activityId:', activityId || '无');
- console.log(' - planId:', planId || '无');
- console.log(' - schemeId:', schemeId || '无');
- console.log(' - finalPlanId (最终使用):', finalPlanId || '无');
- console.log(' - shareUserId:', shareUserId || '无');
- console.log(' - q (扫码链接):', q || '无');
-
- // 处理活动海报二维码(activityId作为qrCode参数值)
- if (activityId && !storeId) {
- console.log('🎯 ===========================================');
- console.log('🎯 [活动二维码] 检测到活动海报二维码');
- console.log('🎯 活动ID (activityId):', activityId);
- console.log('🎯 来源类型: 活动海报');
- console.log('🎯 ===========================================');
-
- // 活动二维码需要跳转到活动页面,不需要跳转到店铺
- wx.setStorageSync('scan_activityId', activityId);
- wx.setStorageSync('need_activity_redirect', true);
- return;
- }
-
- // 如果存在 storeId,说明是扫码进入具体门店(包括异业分享)
- if (storeId) {
- console.log('🏪 ===========================================');
- console.log('🏪 [店铺扫码] 检测到扫码参数');
- console.log('🏪 店铺ID (storeId):', storeId);
- console.log('🏪 扫码次数 (scanCount):', scanCount || '0');
-
- // 确定来源类型
- // 规则:
- // 1. 扫描异业码(partnerId)→ sourceType = "partner"
- // 2. 扫描员工码(employeeId、老板/员工)→ sourceType = "sales" 或 "promoter"
- // 3. 扫描其他码(产品码、案例码等)→ sourceType = "scan"
- let sourceType = 'scan'; // 默认为通用扫码
- let sourceId = null;
-
- if (partnerId) {
- // 异业合作伙伴二维码
- sourceType = 'partner';
- sourceId = partnerId;
- console.log('🤝 [来源识别] 来源类型: 异业合作伙伴 (partner)');
- console.log('🤝 合作伙伴ID (partnerId):', partnerId);
- } else if (employeeId) {
- // 员工二维码(包括老板和员工)
- sourceType = 'sales';
- sourceId = employeeId;
- console.log('👤 [来源识别] 来源类型: 员工展业 (sales)');
- console.log('👤 员工ID (employeeId):', employeeId);
- } else if (ownerId) {
- // 老板二维码
- sourceType = 'sales';
- sourceId = ownerId;
- console.log('👔 [来源识别] 来源类型: 老板展业 (sales)');
- console.log('👔 老板ID (ownerId):', ownerId);
- } else if (userId) {
- // 推广员二维码
- sourceType = 'promoter';
- sourceId = userId;
- console.log('📢 [来源识别] 来源类型: 推广员展业 (promoter)');
- console.log('📢 推广员ID (userId):', userId);
- } else {
- // 其他情况(产品码、案例码、店铺分享等)
- sourceType = 'scan';
- console.log('🏬 [来源识别] 来源类型: 通用扫码 (scan)');
- }
-
- if (productId) {
- console.log('📦 [目标内容] 产品ID (productId):', productId);
- }
-
- if (caseId) {
- console.log('📸 [目标内容] 案例ID (caseId):', caseId);
- }
-
- if (finalPlanId) {
- console.log('📋 [目标内容] 方案ID (planId/schemeId):', finalPlanId);
- console.log('👤 [分享人] 分享用户ID (shareUserId):', shareUserId || '无');
- }
-
- console.log('🏪 ===========================================');
-
- // 保存到临时存储,用于后续跳转和统计
- console.log('💾 [保存扫码信息] 开始保存到本地存储...');
- wx.setStorageSync('scan_storeId', storeId);
- wx.setStorageSync('scan_scanCount', scanCount || '0');
- wx.setStorageSync('scan_sourceType', sourceType);
- wx.setStorageSync('scan_sourceId', sourceId || '');
- wx.setStorageSync('scan_ownerId', ownerId || '');
- wx.setStorageSync('scan_employeeId', employeeId || '');
- wx.setStorageSync('scan_partnerId', partnerId || '');
- wx.setStorageSync('scan_userId', userId || '');
- if (productId) {
- wx.setStorageSync('scan_productId', productId);
- }
- if (caseId) {
- wx.setStorageSync('scan_caseId', caseId);
- }
- if (finalPlanId) {
- wx.setStorageSync('scan_planId', finalPlanId);
- if (shareUserId) {
- wx.setStorageSync('scan_shareUserId', shareUserId);
- }
- }
- wx.setStorageSync('need_scan_redirect', true);
-
- console.log('✅ [保存完成] 扫码信息已保存');
- console.log('📋 [保存内容摘要]:');
- console.log(' - 来源类型:', sourceType);
- console.log(' - 来源ID:', sourceId || '无');
- console.log(' - 店铺ID:', storeId);
- console.log(' - 需要跳转:', true);
- } else if (partnerId) {
- // 理论上异业二维码现在也必须带 storeId,这里仅作为兜底保护
- console.warn('⚠️ 检测到异业合作伙伴二维码缺少 storeId,无法确定门店,请检查二维码生成逻辑');
- }
- },
- /**
- * 判断是否需要跳转到扫码统计页面
- */
- shouldRedirectToScanPage() {
- return wx.getStorageSync('need_scan_redirect') === true;
- },
- /**
- * 跳转到扫码对应的店铺页面
- * 根据 storeId 跳转到对应店铺,同时记录所有来源信息用于统计
- */
- async redirectToScanPage() {
- try {
- console.log('🚀 ===========================================');
- console.log('🚀 [跳转处理] 开始处理扫码跳转');
- console.log('🚀 ===========================================');
-
- // 获取所有扫码相关参数
- const storeId = wx.getStorageSync('scan_storeId');
- const scanCount = wx.getStorageSync('scan_scanCount');
- const sourceType = wx.getStorageSync('scan_sourceType');
- const sourceId = wx.getStorageSync('scan_sourceId');
- const ownerId = wx.getStorageSync('scan_ownerId');
- const employeeId = wx.getStorageSync('scan_employeeId');
- const partnerId = wx.getStorageSync('scan_partnerId');
- const userId = wx.getStorageSync('scan_userId');
- const productId = wx.getStorageSync('scan_productId');
- const caseId = wx.getStorageSync('scan_caseId');
- const planId = wx.getStorageSync('scan_planId');
- const shareUserId = wx.getStorageSync('scan_shareUserId');
-
- console.log('📖 [读取存储] 从本地存储读取扫码信息:');
- console.log(' - storeId:', storeId || '无');
- console.log(' - sourceType:', sourceType || '无');
- console.log(' - sourceId:', sourceId || '无');
- console.log(' - scanCount:', scanCount || '无');
- console.log(' - ownerId:', ownerId || '无');
- console.log(' - employeeId:', employeeId || '无');
- console.log(' - partnerId:', partnerId || '无');
- console.log(' - userId:', userId || '无');
- console.log(' - productId:', productId || '无');
- console.log(' - caseId:', caseId || '无');
- console.log(' - planId:', planId || '无');
- console.log(' - shareUserId:', shareUserId || '无');
-
- // 清除临时存储
- console.log('🧹 [清理存储] 清除临时扫码信息...');
- wx.removeStorageSync('scan_storeId');
- wx.removeStorageSync('scan_scanCount');
- wx.removeStorageSync('scan_sourceType');
- wx.removeStorageSync('scan_sourceId');
- wx.removeStorageSync('scan_ownerId');
- wx.removeStorageSync('scan_employeeId');
- wx.removeStorageSync('scan_partnerId');
- wx.removeStorageSync('scan_userId');
- wx.removeStorageSync('scan_productId');
- wx.removeStorageSync('scan_caseId');
- wx.removeStorageSync('scan_planId');
- wx.removeStorageSync('scan_shareUserId');
- wx.removeStorageSync('need_scan_redirect');
- console.log('✅ [清理完成] 临时存储已清除');
-
- if (!storeId) {
- console.error('❌ [错误] 缺少 storeId 参数,无法跳转');
- return;
- }
-
- console.log('📊 ===========================================');
- console.log('📊 [扫码来源汇总] 扫码进入店铺');
- console.log('📊 店铺 ID:', storeId);
- console.log('📊 来源类型:', sourceType);
- console.log('📊 来源 ID:', sourceId || '无');
- console.log('📊 扫码次数:', scanCount);
- if (ownerId) console.log('📊 老板 ID:', ownerId);
- if (employeeId) console.log('📊 员工 ID:', employeeId);
- if (partnerId) console.log('📊 合作伙伴 ID:', partnerId);
- if (userId) console.log('📊 推广员 ID:', userId);
- if (productId) console.log('📊 产品 ID:', productId);
- if (caseId) console.log('📊 案例 ID:', caseId);
- if (planId) {
- console.log('📊 方案 ID:', planId);
- console.log('📊 分享用户 ID:', shareUserId || '无');
- }
- console.log('📊 ===========================================');
-
- // 设置店铺 ID 到全局和本地存储
- wx.setStorageSync('storeId', storeId);
- getApp().globalData.storeId = storeId;
-
- // 保存来源信息到本地存储(用于后续统计或绑定关系)
- if (sourceId) {
- wx.setStorageSync('scan_from_sourceType', sourceType);
- wx.setStorageSync('scan_from_sourceId', sourceId);
- console.log('✅ 已记录来源信息:', sourceType, sourceId);
- }
-
- // 检查用户是否已登录
- const currentUser = Parse.User.current();
-
- console.log('👤 [登录检查] 检查用户登录状态...');
- console.log(' - 当前用户:', currentUser ? currentUser.id : '未登录');
- console.log(' - 手机号:', currentUser?.get('mobile') || '无');
-
- if (currentUser) {
- // 用户已登录,立即记录扫码统计
- console.log('✅ [已登录] 用户已登录,立即记录扫码统计');
- await this.recordScanStatistics({
- storeId,
- sourceType,
- sourceId,
- ownerId,
- employeeId,
- partnerId,
- userId,
- productId,
- scanCount,
- scanTimestamp: Date.now()
- });
- } else {
- // 用户未登录,保存扫码信息,等登录后再记录
- console.log('⚠️ [未登录] 用户未登录,保存扫码信息待登录后记录');
- const pendingData = {
- storeId,
- sourceType,
- sourceId,
- ownerId,
- employeeId,
- partnerId,
- userId,
- productId,
- scanCount,
- timestamp: Date.now()
- };
- console.log('💾 [待处理记录] 保存内容:', JSON.stringify(pendingData, null, 2));
- wx.setStorageSync('pending_scan_record', pendingData);
- console.log('✅ [保存成功] 待处理扫码记录已保存');
- }
-
- // 如果有产品ID,直接跳转到产品详情的 H5 页面
- if (productId) {
- console.log('🎯 检测到产品ID,跳转到产品详情页');
- await this.redirectToProductDetail(storeId, productId, partnerId);
- return;
- }
-
- // 如果有案例ID,直接跳转到案例详情的 H5 页面
- if (caseId) {
- console.log('🎯 检测到案例ID,跳转到案例详情页');
- await this.redirectToCaseDetail(storeId, caseId, partnerId);
- return;
- }
-
- // 如果有方案ID,跳转到门店首页并传递方案ID
- if (planId) {
- console.log('🎯 检测到方案ID,跳转到门店首页展示方案');
- await this.redirectToStoreWithPlan(storeId, planId, shareUserId, partnerId);
- return;
- }
-
- // 获取默认首页路径并跳转
- let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath;
- url += `?storeId=${storeId}`;
-
- // 如果有产品ID,添加到URL参数中
- if (productId) {
- url += `&productId=${productId}`;
- }
-
- // 如果有异业合作伙伴ID,添加到URL参数中传回web-view
- if (partnerId) {
- url += `&partnerId=${partnerId}`;
- }
-
- console.log('✅ 跳转到店铺页面:', url);
-
- wx.redirectTo({
- url: url,
- fail: (err) => {
- console.error('❌ 跳转失败:', err);
- // 如果 redirectTo 失败,尝试 reLaunch
- wx.reLaunch({
- url: url,
- fail: (err2) => {
- console.error('❌ reLaunch 也失败:', err2);
- }
- });
- }
- });
- } catch (error) {
- console.error('❌ 跳转到店铺页面失败:', error);
- }
- },
-
- /**
- * 跳转到活动页面
- */
- async redirectToActivityPage() {
- try {
- const activityId = wx.getStorageSync('scan_activityId');
-
- // 清除临时存储
- wx.removeStorageSync('scan_activityId');
- wx.removeStorageSync('need_activity_redirect');
-
- if (!activityId) {
- console.error('❌ 缺少 activityId 参数,无法跳转');
- return;
- }
-
- console.log('===========================================');
- console.log('======= 扫码进入活动页面 =======');
- console.log('活动 ID (activityId):', activityId);
- console.log('===========================================');
-
- // 保存活动ID
- wx.setStorageSync('activityId', activityId);
-
- // 获取默认首页路径并跳转(活动页面可能需要根据实际路由调整)
- let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath;
- url += `?activityId=${activityId}`;
-
- console.log('✅ 跳转到活动页面:', url);
-
- wx.redirectTo({
- url: url,
- fail: (err) => {
- console.error('❌ 跳转失败:', err);
- wx.reLaunch({
- url: url,
- fail: (err2) => {
- console.error('❌ reLaunch 也失败:', err2);
- }
- });
- }
- });
- } catch (error) {
- console.error('❌ 跳转到活动页面失败:', error);
- }
- },
-
- /**
- * 跳转到产品详情的 H5 页面
- * @param {string} storeId - 店铺 ID
- * @param {string} productId - 产品 ID
- * @param {string} partnerId - 可选的合作伙伴 ID
- */
- async redirectToProductDetail(storeId, productId, partnerId = null) {
- try {
- console.log('===========================================');
- console.log('======= 跳转到产品详情页 =======');
- console.log('店铺 ID:', storeId);
- console.log('产品 ID:', productId);
- if (partnerId) console.log('合作伙伴 ID:', partnerId);
- console.log('===========================================');
-
- const currentUser = Parse.User.current();
- const token = currentUser ? currentUser.getSessionToken() : null;
-
- // 构建产品详情的 H5 URL(不要在这里编码,后面统一编码)
- let h5Url = `https://app.fmode.cn/dev/pobingfeng/owner/nav/products?storeId=${storeId}`;
-
- // 如果有 token,添加到 URL
- if (token) {
- h5Url += `&token=${token}`;
- } else {
- // 如果没有 token,使用游客模式
- h5Url += `&guestMode=true`;
- }
-
- // 添加产品ID(不要在这里编码,避免双重编码)
- h5Url += `&productId=${productId}`;
-
- // 如果有合作伙伴ID,也添加到URL中
- if (partnerId) {
- h5Url += `&partnerId=${partnerId}`;
- }
-
- console.log('🌐 H5 URL:', h5Url);
-
- // 编码 URL(统一在这里编码一次)
- const encodedUrl = encodeURIComponent(h5Url);
-
- // 构建 web-view 页面路径
- let webViewPath = `/common-page/pages/web-view/index?path=${encodedUrl}&storeId=${storeId}`;
-
- console.log('📄 web-view 页面路径:', webViewPath.substring(0, 100) + '...');
- console.log('===========================================');
-
- wx.redirectTo({
- url: webViewPath,
- success: () => {
- console.log('✅ 跳转到产品详情页成功');
- },
- fail: (err) => {
- console.error('❌ 跳转失败:', err);
- // 如果 redirectTo 失败,尝试 reLaunch
- wx.reLaunch({
- url: webViewPath,
- fail: (err2) => {
- console.error('❌ reLaunch 也失败:', err2);
- }
- });
- }
- });
- } catch (error) {
- console.error('❌ 跳转到产品详情页失败:', error);
- }
- },
-
- /**
- * 跳转到案例详情的 H5 页面
- * @param {string} storeId - 店铺 ID
- * @param {string} caseId - 案例 ID
- * @param {string} partnerId - 可选的合作伙伴 ID
- */
- async redirectToCaseDetail(storeId, caseId, partnerId = null) {
- try {
- console.log('===========================================');
- console.log('======= 跳转到案例详情页 =======');
- console.log('店铺 ID:', storeId);
- console.log('案例 ID:', caseId);
- if (partnerId) console.log('合作伙伴 ID:', partnerId);
- console.log('===========================================');
-
- const currentUser = Parse.User.current();
- const token = currentUser ? currentUser.getSessionToken() : null;
-
- // 构建案例详情的 H5 URL(不要在这里编码,后面统一编码)
- let h5Url = `https://app.fmode.cn/dev/pobingfeng/owner/nav/cases?storeId=${storeId}`;
-
- // 如果有 token,添加到 URL
- if (token) {
- h5Url += `&token=${token}`;
- } else {
- // 如果没有 token,使用游客模式
- h5Url += `&guestMode=true`;
- }
-
- // 添加案例ID(不要在这里编码,避免双重编码)
- h5Url += `&caseId=${caseId}`;
-
- // 如果有合作伙伴ID,也添加到URL中
- if (partnerId) {
- h5Url += `&partnerId=${partnerId}`;
- }
-
- console.log('🌐 H5 URL:', h5Url);
-
- // 编码 URL(统一在这里编码一次)
- const encodedUrl = encodeURIComponent(h5Url);
-
- // 构建 web-view 页面路径
- let webViewPath = `/common-page/pages/web-view/index?path=${encodedUrl}&storeId=${storeId}`;
-
- console.log('📄 web-view 页面路径:', webViewPath.substring(0, 100) + '...');
- console.log('===========================================');
-
- wx.redirectTo({
- url: webViewPath,
- success: () => {
- console.log('✅ 跳转到案例详情页成功');
- },
- fail: (err) => {
- console.error('❌ 跳转失败:', err);
- // 如果 redirectTo 失败,尝试 reLaunch
- wx.reLaunch({
- url: webViewPath,
- fail: (err2) => {
- console.error('❌ reLaunch 也失败:', err2);
- }
- });
- }
- });
- } catch (error) {
- console.error('❌ 跳转到案例详情页失败:', error);
- }
- },
-
- /**
- * 跳转到门店首页并展示方案
- * @param {string} storeId - 店铺 ID
- * @param {string} planId - 方案 ID
- * @param {string} shareUserId - 分享方案的用户 ID
- * @param {string} partnerId - 可选的合作伙伴 ID
- */
- async redirectToStoreWithPlan(storeId, planId, shareUserId = null, partnerId = null) {
- try {
- console.log('===========================================');
- console.log('======= 跳转到门店首页展示方案 =======');
- console.log('店铺 ID:', storeId);
- console.log('方案 ID:', planId);
- if (shareUserId) console.log('分享用户 ID:', shareUserId);
- if (partnerId) console.log('合作伙伴 ID:', partnerId);
- console.log('===========================================');
-
- const currentUser = Parse.User.current();
- const token = currentUser ? currentUser.getSessionToken() : null;
-
- // 构建门店首页的 H5 URL,带上方案ID
- let h5Url = `https://app.fmode.cn/dev/pobingfeng/owner/nav/home?storeId=${storeId}`;
-
- // 如果有 token,添加到 URL
- if (token) {
- h5Url += `&token=${token}`;
- } else {
- // 如果没有 token,使用游客模式
- h5Url += `&guestMode=true`;
- }
-
- // 添加方案ID(使用planId参数名)
- h5Url += `&planId=${planId}`;
-
- // 如果有分享用户ID,也添加到URL中
- if (shareUserId) {
- h5Url += `&shareUserId=${shareUserId}`;
- }
-
- // 如果有合作伙伴ID,也添加到URL中
- if (partnerId) {
- h5Url += `&partnerId=${partnerId}`;
- }
-
- console.log('🌐 H5 URL:', h5Url);
-
- // 编码 URL
- const encodedUrl = encodeURIComponent(h5Url);
-
- // 构建 web-view 页面路径
- let webViewPath = `/common-page/pages/web-view/index?path=${encodedUrl}&storeId=${storeId}`;
-
- console.log('📄 web-view 页面路径:', webViewPath.substring(0, 100) + '...');
- console.log('===========================================');
-
- wx.redirectTo({
- url: webViewPath,
- success: () => {
- console.log('✅ 跳转到门店首页展示方案成功');
- },
- fail: (err) => {
- console.error('❌ 跳转失败:', err);
- // 如果 redirectTo 失败,尝试 reLaunch
- wx.reLaunch({
- url: webViewPath,
- fail: (err2) => {
- console.error('❌ reLaunch 也失败:', err2);
- }
- });
- }
- });
- } catch (error) {
- console.error('❌ 跳转到门店首页展示方案失败:', error);
- }
- },
-
- /**
- * 记录扫码统计信息
- * @param {Object} params - 扫码参数对象
- * @param {string} params.storeId - 店铺 ID
- * @param {string} params.sourceType - 来源类型 (employee/owner/partner/promoter/store)
- * @param {string} params.sourceId - 来源 ID
- * @param {string} params.ownerId - 老板 ID
- * @param {string} params.employeeId - 员工 ID
- * @param {string} params.partnerId - 合作伙伴 ID
- * @param {string} params.userId - 推广员 ID
- * @param {string} params.productId - 产品 ID
- * @param {string} params.scanCount - 扫码次数
- */
- async recordScanStatistics(params) {
- try {
- console.log('📝 ===========================================');
- console.log('📝 [扫码统计] 开始记录扫码统计');
- console.log('📝 ===========================================');
-
- const {
- storeId,
- sourceType,
- sourceId,
- ownerId,
- employeeId,
- partnerId,
- userId,
- productId,
- scanCount,
- scanTimestamp
- } = params;
-
- console.log('📋 [统计参数] 接收到的参数:');
- console.log(' - storeId:', storeId);
- console.log(' - sourceType:', sourceType);
- console.log(' - sourceId:', sourceId || '无');
- console.log(' - ownerId:', ownerId || '无');
- console.log(' - employeeId:', employeeId || '无');
- console.log(' - partnerId:', partnerId || '无');
- console.log(' - userId:', userId || '无');
- console.log(' - productId:', productId || '无');
- console.log(' - scanCount:', scanCount || '0');
-
- // 检查用户是否已登录
- const currentUser = Parse.User.current();
- if (!currentUser) {
- console.log('⚠️ [未登录] 用户未登录,不记录扫码统计');
- return;
- }
-
- console.log('👤 [用户信息] 当前登录用户:');
- console.log(' - 用户ID:', currentUser.id);
- console.log(' - 手机号:', currentUser.get('mobile') || '无');
-
- // 如果没有来源信息,不记录统计
- if (!sourceId && !ownerId && !employeeId && !partnerId && !userId) {
- console.log('ℹ️ [跳过] 无来源信息,跳过统计记录');
- return;
- }
- const hasMobile = !!currentUser.get('mobile');
- const scanTs = typeof scanTimestamp === 'number' && scanTimestamp > 0 ? scanTimestamp : Date.now();
- if (partnerId && !hasMobile) {
- const pendingData = {
- storeId,
- sourceType,
- sourceId,
- ownerId,
- employeeId,
- partnerId,
- userId,
- productId,
- scanCount,
- timestamp: scanTs
- };
- try {
- const existingPending = wx.getStorageSync('pending_scan_record');
- if (!existingPending || existingPending.partnerId !== partnerId || existingPending.storeId !== storeId) {
- wx.setStorageSync('pending_scan_record', pendingData);
- }
- } catch (e) {
- try {
- wx.setStorageSync('pending_scan_record', pendingData);
- } catch (e2) {}
- }
- console.log('⏳ [异业扫码] 用户未绑定手机号,延后到注册登录后再统计');
- return;
- }
-
- console.log('✅ [开始记录] 用户已登录且有来源信息,开始记录');
-
- // 创建扫码记录
- const ScanRecord = Parse.Object.extend('ScanRecord');
- const record = new ScanRecord();
-
- record.set('company', {
- __type: 'Pointer',
- className: 'Company',
- objectId: getApp().globalData.company
- });
- record.set('storeId', storeId);
- record.set('sourceType', sourceType || 'unknown');
- record.set('scanCount', parseInt(scanCount) || 0);
- record.set('scanTime', new Date());
-
- console.log('📦 [记录内容] 准备保存到 ScanRecord 表:');
- console.log(' - company:', getApp().globalData.company);
- console.log(' - storeId:', storeId);
- console.log(' - sourceType:', sourceType || 'unknown');
- console.log(' - scanCount:', parseInt(scanCount) || 0);
- console.log(' - scanTime:', new Date().toISOString());
-
- // 根据来源类型设置对应的ID
- if (ownerId) {
- record.set('ownerId', ownerId);
- console.log(' - ownerId:', ownerId);
- }
- if (employeeId) {
- record.set('employeeId', employeeId);
- console.log(' - employeeId:', employeeId);
- }
- if (partnerId) {
- record.set('partnerId', partnerId);
- console.log(' - partnerId:', partnerId);
- }
- if (userId) {
- record.set('userId', userId);
- console.log(' - userId:', userId);
- }
- if (productId) {
- record.set('productId', productId);
- console.log(' - productId:', productId);
- }
-
- // 记录扫码用户
- record.set('user', {
- __type: 'Pointer',
- className: '_User',
- objectId: currentUser.id
- });
- console.log(' - user:', currentUser.id);
-
- await record.save();
- console.log('✅ [保存成功] 扫码记录已保存到数据库');
- console.log(' - 记录ID:', record.id);
- // 如果存在异业合作伙伴ID,处理异业绑定和扫码次数
- if (partnerId) {
- try {
- console.log('🤝 ===========================================');
- console.log('🤝 [异业处理] 开始处理异业扫码逻辑');
- console.log('🤝 用户ID:', currentUser.id);
- console.log('🤝 异业ID:', partnerId);
- console.log('🤝 门店ID:', storeId);
- console.log('🤝 ===========================================');
-
- // 检查用户是否已经绑定了异业合作伙伴
- const userBoundPartner = currentUser.get('Partner');
- const userBoundPartnerId = userBoundPartner ? userBoundPartner.id : null;
-
- console.log('🔍 [绑定检查] 用户已绑定的异业ID:', userBoundPartnerId || '无');
-
- // 规则1:如果用户已绑定其他异业,不处理当前异业的扫码
- if (userBoundPartnerId && userBoundPartnerId !== partnerId) {
- console.log('⚠️ [规则1] 用户已绑定其他异业,不处理当前异业的扫码');
- console.log(' - 已绑定异业:', userBoundPartnerId);
- console.log(' - 当前扫码异业:', partnerId);
- console.log('🤝 ===========================================');
- return;
- }
-
- // 规则2:检查该用户在该门店是否已经扫过该异业的码
- console.log('🔍 [重复检查] 检查是否已扫过该异业的码...');
- const ScanRecord = Parse.Object.extend('ScanRecord');
- const scanQuery = new Parse.Query(ScanRecord);
- scanQuery.equalTo('user', {
- __type: 'Pointer',
- className: '_User',
- objectId: currentUser.id
- });
- scanQuery.equalTo('partnerId', partnerId);
- scanQuery.equalTo('storeId', storeId);
- scanQuery.equalTo('partnerNewUserCounted', true);
-
- const existingScan = await scanQuery.first();
-
- if (existingScan) {
- console.log('⚠️ [规则2] 该用户在该门店已计入“异业新用户扫码”,不重复计数');
- console.log(' - 计入时间:', existingScan.get('scanTime'));
- console.log(' - 记录ID:', existingScan.id);
- console.log('🤝 ===========================================');
- return;
- }
-
- let partnerNewUserQualified = false;
- try {
- const createdAtMs = currentUser.createdAt instanceof Date ? currentUser.createdAt.getTime() : 0;
- const diffMs = createdAtMs ? Math.abs(scanTs - createdAtMs) : Number.MAX_SAFE_INTEGER;
- partnerNewUserQualified = diffMs <= 10 * 60 * 1000;
- } catch (e) {
- partnerNewUserQualified = false;
- }
- if (!partnerNewUserQualified) {
- console.log('ℹ️ [规则2] 非“扫码注册登录的新用户”,不计入异业扫码次数');
- console.log('🤝 ===========================================');
- return;
- }
- record.set('partnerNewUserCounted', true);
- console.log('✅ [首次计入] 该用户在该门店首次计入“异业新用户扫码”');
-
- // 规则3:如果用户还没有绑定异业,绑定当前异业
- if (!userBoundPartnerId) {
- console.log('🔗 [规则3] 用户首次扫异业码,开始绑定异业合作伙伴');
- console.log(' - 异业ID:', partnerId);
-
- currentUser.set('Partner', {
- __type: 'Pointer',
- className: 'Partner',
- objectId: partnerId
- });
- await currentUser.save();
- console.log('✅ [绑定成功] 异业绑定成功');
- } else {
- console.log('ℹ️ [已绑定] 用户已绑定该异业,无需重复绑定');
- }
-
- // 规则4:为该异业在该门店增加扫码次数
- console.log('📊 [规则4] 开始更新异业扫码次数...');
- const Partner = Parse.Object.extend('Partner');
- const partnerQuery = new Parse.Query(Partner);
- const partnerObj = await partnerQuery.get(partnerId);
- if (partnerObj) {
- console.log('📋 [异业信息] 找到异业合作伙伴记录');
- console.log(' - 异业ID:', partnerObj.id);
- console.log(' - 异业名称:', partnerObj.get('name') || '未设置');
-
- // 获取或初始化门店扫码次数映射
- let storeScans = partnerObj.get('storeScans') || {};
- const oldCount = storeScans[storeId] || 0;
-
- // 为该门店的扫码次数 +1
- storeScans[storeId] = oldCount + 1;
-
- partnerObj.set('storeScans', storeScans);
-
- // 同时更新总扫码次数
- const oldTotalCount = partnerObj.get('scanCount') || 0;
- partnerObj.increment('scanCount', 1);
-
- await partnerObj.save();
-
- console.log('✅ [更新成功] 异业扫码次数已更新');
- console.log(' - 门店ID:', storeId);
- console.log(' - 该门店扫码次数: %d → %d', oldCount, storeScans[storeId]);
- console.log(' - 总扫码次数: %d → %d', oldTotalCount, oldTotalCount + 1);
- } else {
- console.error('❌ [错误] 未找到异业合作伙伴记录');
- }
- if (record && record.get && record.get('partnerNewUserCounted') === true) {
- await record.save();
- }
-
- console.log('🤝 ===========================================');
- } catch (e) {
- console.error('❌ [异业处理失败] 处理异业合作伙伴绑定失败:', e);
- console.error(' - 错误信息:', e.message);
- console.error(' - 错误堆栈:', e.stack);
- console.log('🤝 ===========================================');
- }
- }
- } catch (error) {
- console.warn('⚠️ 保存扫码记录失败:', error);
- // 不影响主流程
- }
- }
- });
- function normalizeCnMobile(value) {
- if (value === null || value === undefined) return '';
- const digits = String(value).replace(/\D/g, '');
- if (!digits) return '';
- const last11 = digits.length >= 11 ? digits.slice(-11) : digits;
- if (/^1\d{10}$/.test(last11)) return last11;
- if (/^1\d{10}$/.test(digits)) return digits;
- return '';
- }
- function addCnMobileToSet(set, value) {
- const m = normalizeCnMobile(value);
- if (m) set.add(m);
- }
- function addCnMobilesFromValue(set, value) {
- if (!value) return;
- if (Array.isArray(value)) {
- value.forEach((v) => addCnMobileToSet(set, v));
- return;
- }
- addCnMobileToSet(set, value);
- }
- function addCnMobilesFromParseObject(set, parseObj, keys) {
- if (!parseObj || typeof parseObj.get !== 'function') return;
- keys.forEach((k) => {
- try {
- const v = parseObj.get(k);
- addCnMobilesFromValue(set, v);
- } catch (e) {}
- });
- }
- async function addCnMobileFromUserPointer(set, Parse, pointer) {
- if (!pointer) return;
- if (typeof pointer.get === 'function') {
- addCnMobileToSet(set, pointer.get('mobile'));
- addCnMobileToSet(set, pointer.get('phone'));
- }
- const pointerId = pointer && pointer.id ? pointer.id : null;
- if (!pointerId) return;
- try {
- const q = new Parse.Query('_User');
- q.select('mobile');
- const u = await q.get(pointerId);
- if (u) addCnMobileToSet(set, u.get('mobile'));
- } catch (e) {}
- }
- function collectUserMobiles(currentUser) {
- const set = new Set();
- addCnMobileToSet(set, currentUser && typeof currentUser.get === 'function' ? currentUser.get('mobile') : null);
- addCnMobileToSet(set, currentUser && typeof currentUser.get === 'function' ? currentUser.get('phone') : null);
- try {
- const storageMobile = wx.getStorageSync('user_mobile');
- addCnMobileToSet(set, storageMobile);
- } catch (e) {}
- return Array.from(set);
- }
- function setSample(set, limit) {
- const arr = [];
- let i = 0;
- for (const v of set) {
- arr.push(v);
- i++;
- if (i >= (limit || 5)) break;
- }
- return arr;
- }
- function intersectArrSet(arr, set) {
- return arr.filter((v) => set.has(v));
- }
- async function collectPartnerPhonesForStore(Parse, storeId) {
- const phones = new Set();
- const mobiles = new Set();
- try {
- if (!storeId) return { phones, mobiles };
- const currentUser = Parse.User.current();
- const sessionToken = currentUser && typeof currentUser.getSessionToken === 'function'
- ? currentUser.getSessionToken()
- : null;
- const requestOptions = sessionToken ? { sessionToken } : undefined;
- const storePointer = new Parse.Object('ShopStore'); storePointer.id = storeId;
- const q = new Parse.Query('Partner');
- q.equalTo('store', storePointer);
- q.limit(200);
- q.select('phone', 'mobile');
- const list = await q.find(requestOptions);
- if (list && list.length) {
- list.forEach((p) => {
- addCnMobileToSet(phones, p.get('phone'));
- addCnMobileToSet(mobiles, p.get('mobile'));
- });
- }
- } catch (e) {
- console.warn('⚠️ [traffic] Partner 查询失败:', e?.code, e?.message || e);
- }
- return { phones, mobiles };
- }
- async function collectTrafficWhitelistMobiles(Parse, storeId) {
- const set = new Set();
- const mobileKeys = [
- 'mobile',
- 'phone'
- ];
- const userPointerKeys = [
- 'owner',
- 'boss',
- 'admin',
- 'manager',
- 'user',
- 'ownerUser',
- 'bossUser',
- 'adminUser',
- 'managerUser',
- 'createdBy'
- ];
-
- try {
- if (storeId) {
- const q = new Parse.Query('ShopStore');
- q.equalTo('objectId', storeId);
- q.select(...mobileKeys, ...userPointerKeys);
- userPointerKeys.forEach((k) => q.include(k));
- const storeObj = await q.first();
- if (storeObj) {
- addCnMobilesFromParseObject(set, storeObj, mobileKeys);
- for (const k of userPointerKeys) {
- await addCnMobileFromUserPointer(set, Parse, storeObj.get(k));
- }
- }
- }
- } catch (e) {}
- const userStaffPhoneKeys = ['mobile', 'phone'];
- try {
- if (storeId) {
- const flags = getApp().globalData || {};
- const disabled = !!flags.disableUserStaffQuery || wx.getStorageSync('disableUserStaffQuery') === true;
- if (disabled) {
- console.log('⚠️ [traffic] userStaff 查询已禁用(检测到类不存在/无权限)');
- } else {
- const currentUser = Parse.User.current();
- const sessionToken = currentUser && typeof currentUser.getSessionToken === 'function'
- ? currentUser.getSessionToken()
- : null;
- const requestOptions = sessionToken ? { sessionToken } : undefined;
- const companyId = getApp().globalData.company;
- const companyPointer = new Parse.Object('Company'); companyPointer.id = companyId;
- const q = new Parse.Query('UserStaff');
- q.equalTo('company', companyPointer);
- q.limit(200);
- q.select(...userStaffPhoneKeys);
- const list = await q.find(requestOptions);
- if (list && list.length) {
- list.forEach((staff) => addCnMobilesFromParseObject(set, staff, userStaffPhoneKeys));
- }
- }
- }
- } catch (e) {
- console.warn('⚠️ [traffic] userStaff 查询失败:', e?.code, e?.message || e);
- try {
- const msg = (e?.message || '').toLowerCase();
- if (
- e?.code === 119 ||
- msg.includes('non-existent class') ||
- msg.includes('class: userstaff') ||
- msg.includes('class: userstaff') ||
- msg.includes('class: userstaff')
- ) {
- getApp().globalData.disableUserStaffQuery = true;
- wx.setStorageSync('disableUserStaffQuery', true);
- console.warn('⚠️ [traffic] 已禁用后续 userStaff 查询(类不存在或无权限)');
- }
- } catch (_) {}
- }
- const partnerPhoneKeys = ['mobile', 'phone'];
- try {
- if (storeId) {
- const currentUser = Parse.User.current();
- const sessionToken = currentUser && typeof currentUser.getSessionToken === 'function'
- ? currentUser.getSessionToken()
- : null;
- const requestOptions = sessionToken ? { sessionToken } : undefined;
- const storePointer = new Parse.Object('ShopStore'); storePointer.id = storeId;
- const q = new Parse.Query('Partner');
- q.equalTo('store', storePointer);
- q.limit(200);
- q.select(...partnerPhoneKeys);
- const list = await q.find(requestOptions);
- if (list && list.length) {
- list.forEach((p) => addCnMobilesFromParseObject(set, p, partnerPhoneKeys));
- }
- }
- } catch (e) {
- console.warn('⚠️ [traffic] Partner 查询失败:', e?.code, e?.message || e);
- }
- return set;
- }
- async function isTrafficExemptForStore(Parse, storeId, currentUser) {
- try {
- if (!currentUser) return false;
- if (currentUser.get('trafficExempt') === true) return true;
- const userNumbers = collectUserMobiles(currentUser);
- const whitelist = await collectTrafficWhitelistMobiles(Parse, storeId);
- const partnerSets = await collectPartnerPhonesForStore(Parse, storeId);
- console.log('🧾 [traffic] 白名单判定开始:', {
- storeId,
- userId: currentUser.id,
- userNumbers
- });
- console.log('🧾 [traffic] Partner 集合:', {
- phoneCount: partnerSets.phones.size,
- phoneSample: setSample(partnerSets.phones, 5),
- mobileCount: partnerSets.mobiles.size,
- mobileSample: setSample(partnerSets.mobiles, 5)
- });
- const partnerPhoneMatches = intersectArrSet(userNumbers, partnerSets.phones);
- if (partnerPhoneMatches.length) {
- console.log('🧾 [traffic] 命中 Partner.phone:', {
- matches: partnerPhoneMatches
- });
- return true;
- }
- const partnerMobileMatches = intersectArrSet(userNumbers, partnerSets.mobiles);
- if (partnerMobileMatches.length) {
- console.log('🧾 [traffic] 命中 Partner.mobile:', {
- matches: partnerMobileMatches
- });
- return true;
- }
- console.log('🧾 [traffic] 聚合白名单:', {
- size: whitelist.size,
- sample: setSample(whitelist, 10)
- });
- const whitelistMatches = intersectArrSet(userNumbers, whitelist);
- if (whitelistMatches.length) {
- console.log('🧾 [traffic] 命中聚合白名单:', {
- matches: whitelistMatches
- });
- return true;
- }
- console.log('🧾 [traffic] 白名单未命中');
- return false;
- } catch (e) {
- console.warn('⚠️ [traffic] 白名单判定失败,按非白名单处理:', e?.message || e);
- return false;
- }
- }
- async function decrementStoreTrafficImpl(Parse, storeId) {
- const currentUser = Parse.User.current();
- const sessionToken = currentUser && typeof currentUser.getSessionToken === 'function'
- ? currentUser.getSessionToken()
- : null;
- const requestOptions = sessionToken ? { sessionToken } : undefined;
- console.log('🧾 [traffic] 开始扣减 ShopStore.traffic');
- console.log('🧾 [traffic] storeId:', storeId);
- console.log('🧾 [traffic] userId:', currentUser ? currentUser.id : '无用户');
- console.log('🧾 [traffic] hasSessionToken:', sessionToken ? 'true' : 'false');
- const storeQuery = new Parse.Query('ShopStore');
- const store = await storeQuery.get(storeId, requestOptions);
- if (!store) {
- console.error('🧾 [traffic] 未找到门店记录, storeId:', storeId);
- throw new Error('未找到门店记录');
- }
- const trafficValue = store.get('traffic');
- const trafficNumber = Number(trafficValue);
- const traffic = Number.isFinite(trafficNumber) ? trafficNumber : 0;
- const next = Math.max(traffic - 1, 0);
- console.log('🧾 [traffic] before trafficValue:', trafficValue);
- console.log('🧾 [traffic] parsed traffic:', traffic);
- console.log('🧾 [traffic] next:', next);
- if (next !== traffic || trafficValue === undefined) {
- store.set('traffic', next);
- const saved = await store.save(null, requestOptions);
- console.log('🧾 [traffic] 保存成功 storeObjectId:', saved && saved.id ? saved.id : store.id);
- }
- console.log('🧾 [traffic] 结束扣减 ShopStore.traffic');
- }
- async function isInternalVisit(Parse, storeId, currentUser, ids) {
- try {
- if (!currentUser) return false;
- const { ownerId, employeeId, partnerId } = ids || {};
- const roles = Array.isArray(currentUser.get('roles')) ? currentUser.get('roles') : [];
- const roleSet = new Set(roles.map(r => String(r).toLowerCase()));
- const isOwnerRole = roleSet.has('owner') || roleSet.has('boss');
- const isEmployeeRole = roleSet.has('employee') || roleSet.has('staff') || roleSet.has('sales');
- const isAdminStaffRole = roleSet.has('userstaff') || roleSet.has('admin_staff') || roleSet.has('manager_staff');
- const isPartnerRole = roleSet.has('partner') || roleSet.has('partner_admin') || roleSet.has('partner_staff');
- if (partnerId && isPartnerRole) return true;
- if (employeeId && isEmployeeRole) return true;
- if (isAdminStaffRole) return true;
- const storeQuery = new Parse.Query('ShopStore');
- const store = await storeQuery.get(storeId);
- const storeOwner = store ? store.get('user') : null;
- if (storeOwner && storeOwner.id === currentUser.id) return true;
- if (ownerId && isOwnerRole) return true;
- if (employeeId && currentUser.id === employeeId) return true;
- if (ownerId && currentUser.id === ownerId) return true;
- return false;
- } catch (e) {
- return false;
- }
- }
|