0235699曾露 1 week ago
parent
commit
e9931fd3cb
6 changed files with 575 additions and 91 deletions
  1. 85 1
      app.js
  2. 280 37
      components/app-auth/index.js
  3. 7 1
      components/diy-my/my.js
  4. 13 10
      components/diy-personal/index.js
  5. 157 41
      index.js
  6. 33 1
      nova-pbf/pages/index/index.js

+ 85 - 1
app.js

@@ -301,6 +301,80 @@ App({
     if(referrerInfo?.extraData?.status && referrerInfo.extraData.status == 'success'){
       getApp().globalData.merchant_trade_no = referrerInfo.extraData.req_extradata.merchant_trade_no
     }
+    try {
+      const pending = wx.getStorageSync('pending_scan_record');
+      if (!pending) return;
+
+      const now = Date.now();
+      const lastTry = wx.getStorageSync('pending_scan_record_last_try') || 0;
+      if (lastTry && now - lastTry < 2000) return;
+      wx.setStorageSync('pending_scan_record_last_try', now);
+
+      const currentUser = Parse.User.current();
+      if (!currentUser || !currentUser.id) {
+        console.log('⚠️ [ScanRecord] 全局补记跳过:无登录用户');
+        return;
+      }
+
+      let storageMobile = '';
+      try { storageMobile = wx.getStorageSync('user_mobile') || ''; } catch (e) {}
+      const mobile = normalizeCnMobile(currentUser.get('mobile') || currentUser.get('phone') || storageMobile);
+      if (!mobile) {
+        console.log('⏳ [ScanRecord] 全局补记等待:仍无手机号');
+        return;
+      }
+
+      const scanTime = pending.timestamp || 0;
+      const hoursPassed = scanTime ? (now - scanTime) / (1000 * 60 * 60) : 0;
+      if (hoursPassed > 24) {
+        console.log('⚠️ [ScanRecord] 全局补记清理:pending_scan_record 超过24小时');
+        wx.removeStorageSync('pending_scan_record');
+        return;
+      }
+
+      const ScanRecord = Parse.Object.extend('ScanRecord');
+      const record = new ScanRecord();
+
+      record.set('company', {
+        __type: 'Pointer',
+        className: 'Company',
+        objectId: this.globalData.company
+      });
+      record.set('storeId', pending.storeId);
+      record.set('sourceType', pending.sourceType || 'unknown');
+      record.set('scanCount', parseInt(pending.scanCount) || 0);
+      record.set('scanTime', new Date());
+
+      if (pending.employeeId) record.set('employeeId', pending.employeeId);
+      if (pending.partnerId) record.set('partnerId', pending.partnerId);
+      if (pending.userId) record.set('userId', pending.userId);
+      if (pending.productId) record.set('productId', pending.productId);
+
+      record.set('user', {
+        __type: 'Pointer',
+        className: '_User',
+        objectId: currentUser.id
+      });
+      record.set('userid', currentUser.id);
+
+      console.log('🧾 [ScanRecord] 全局补记开始:', {
+        userId: currentUser.id,
+        storeId: pending.storeId || '无',
+        partnerId: pending.partnerId || '无',
+        mobile: mobile || '无'
+      });
+
+      record.save().then((saved) => {
+        console.log('✅ [ScanRecord] 全局补记成功:', { recordId: saved && saved.id ? saved.id : record.id });
+        wx.removeStorageSync('pending_scan_record');
+      }).catch((e) => {
+        console.error('❌ [ScanRecord] 全局补记失败:', e?.message || e);
+        try {
+          console.error('   - code:', e.code);
+          console.error('   - details:', e.details);
+        } catch (e2) {}
+      });
+    } catch (e) {}
   },
   autoUpdate() {
     let self = this
@@ -384,4 +458,14 @@ App({
     }
   }
 
-})
+})
+
+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 '';
+}

+ 280 - 37
components/app-auth/index.js

@@ -86,6 +86,89 @@ async function localRejudgeIfNeeded() {
     console.warn('🚦 [traffic] 授权页本地复判失败:', e?.message || e);
   }
 }
+
+async function flushPendingScanRecord(mobileHint) {
+  try {
+    const pending = wx.getStorageSync('pending_scan_record');
+    if (!pending) {
+      console.log('ℹ️ [ScanRecord] 授权页未发现 pending_scan_record');
+      return false;
+    }
+
+    const now = Date.now();
+    const scanTime = pending.timestamp || 0;
+    const hoursPassed = scanTime ? (now - scanTime) / (1000 * 60 * 60) : 0;
+    if (hoursPassed > 24) {
+      console.log('⚠️ [ScanRecord] pending_scan_record 已超过24小时,清除');
+      wx.removeStorageSync('pending_scan_record');
+      return false;
+    }
+
+    const currentUser = Parse.User.current();
+    if (!currentUser || !currentUser.id) {
+      console.log('⚠️ [ScanRecord] 授权页补记失败:无登录用户');
+      return false;
+    }
+
+    let storageMobile = '';
+    try { storageMobile = wx.getStorageSync('user_mobile') || ''; } catch (e) {}
+    const normalized = normalizeCnMobile(
+      currentUser.get('mobile') || currentUser.get('phone') || storageMobile || mobileHint
+    );
+    console.log('📌 [ScanRecord] 授权页补记前置:', {
+      userId: currentUser.id,
+      mobile: normalizeCnMobile(currentUser.get('mobile')) || '无',
+      phone: normalizeCnMobile(currentUser.get('phone')) || '无',
+      storage: normalizeCnMobile(storageMobile) || '无',
+      hint: normalizeCnMobile(mobileHint) || '无',
+      normalized: normalized || '无',
+      storeId: pending.storeId || '无',
+      partnerId: pending.partnerId || '无'
+    });
+
+    if (!normalized) {
+      console.log('⏳ [ScanRecord] 授权页补记失败:仍未获取到手机号');
+      return false;
+    }
+
+    const ScanRecord = Parse.Object.extend('ScanRecord');
+    const record = new ScanRecord();
+
+    record.set('company', {
+      __type: 'Pointer',
+      className: 'Company',
+      objectId: company
+    });
+    record.set('storeId', pending.storeId);
+    record.set('sourceType', pending.sourceType || 'unknown');
+    record.set('scanCount', parseInt(pending.scanCount) || 0);
+    record.set('scanTime', new Date());
+
+    if (pending.employeeId) record.set('employeeId', pending.employeeId);
+    if (pending.partnerId) record.set('partnerId', pending.partnerId);
+    if (pending.userId) record.set('userId', pending.userId);
+    if (pending.productId) record.set('productId', pending.productId);
+
+    record.set('user', {
+      __type: 'Pointer',
+      className: '_User',
+      objectId: currentUser.id
+    });
+    record.set('userid', currentUser.id);
+
+    await record.save();
+    console.log('✅ [ScanRecord] 授权页补记保存成功', { recordId: record.id });
+    wx.removeStorageSync('pending_scan_record');
+    return true;
+  } catch (e) {
+    console.error('❌ [ScanRecord] 授权页补记保存失败:', e?.message || e);
+    try {
+      console.error('   - code:', e.code);
+      console.error('   - details:', e.details);
+    } catch (e2) {}
+    return false;
+  }
+}
 /**
  * @class NovaAppAuth
  * @memberof module:components
@@ -241,14 +324,30 @@ Page({
     let {
       code
     } = e.detail
-    console.log(code);
+    console.log('📱 [mobile] getPhoneNumber code:', code || '无');
+    if (!code) {
+      console.error('❌ [mobile] getPhoneNumber 缺少 code,无法换取手机号');
+      return
+    }
     let phoneNumber = await request.getPhone(code)
-    if(phoneNumber) this.authMobileUser(phoneNumber)
+    console.log('📱 [mobile] request.getPhone 返回:', phoneNumber || '无');
+    if(phoneNumber) {
+      await this.authMobileUser(phoneNumber)
+    } else {
+      console.error('❌ [mobile] 未获取到手机号(request.getPhone 返回空)');
+    }
   },
 
   //合并User与绑定手机号逻辑
   async authMobileUser(mobile) {
+    let pendingScan = null
+    try { pendingScan = wx.getStorageSync('pending_scan_record'); } catch (e) {}
     let currentUser = Parse.User.current()
+    console.log('📱 [mobile] 开始处理手机号绑定/合并:', {
+      userId: currentUser?.id || '无',
+      mobile: mobile || '无',
+      hasPendingScan: !!pendingScan
+    })
     let queryMobUser = new Parse.Query('_User')
     queryMobUser.equalTo('mobile', mobile)
     queryMobUser.equalTo('company', company)
@@ -265,21 +364,52 @@ Page({
           // let user = Parse.User.current();
           wx.setStorageSync("userLogin", user.id);
           try {
-            wx.setStorageSync('user_mobile', user.get('mobile') || '');
+            const hasMobile = !!normalizeCnMobile(user.get('mobile') || user.get('phone') || mobile);
+            if (hasMobile && !normalizeCnMobile(user.get('mobile'))) {
+              user.set('mobile', mobile);
+            }
+            if (hasMobile && !normalizeCnMobile(user.get('phone'))) {
+              user.set('phone', mobile);
+            }
+            if (hasMobile) {
+              await user.save();
+            }
+            console.log('✅ [mobile] merge-become 后用户手机号状态:', {
+              userId: user?.id || '无',
+              mobile: user.get('mobile') || '无',
+              phone: user.get('phone') || '无'
+            })
+          } catch (e) {
+            console.error('❌ [mobile] merge-become 后写入手机号失败:', e?.message || e);
+          }
+          try {
+            wx.setStorageSync('user_mobile', (user.get('mobile') || mobile) || '');
           } catch (e) {}
           try {
-            if (typeof getApp().checkTrafficDeductPending === 'function') {
-              console.log('🚦 [traffic] 触发暂缓扣减复判(merge-become 成功后)');
-              await getApp().checkTrafficDeductPending();
+            await flushPendingScanRecord(mobile);
+          } catch (e) {}
+          try {
+            if (typeof getApp().checkAndRecordPendingScan === 'function') {
+              const ok = await getApp().checkAndRecordPendingScan();
+              console.log('📌 [ScanRecord] merge-become 补记结果:', ok ? 'success' : 'not_written');
+              setTimeout(() => {
+                try { getApp().checkAndRecordPendingScan(); } catch (e) {}
+              }, 500);
+              setTimeout(() => {
+                try { getApp().checkAndRecordPendingScan(); } catch (e) {}
+              }, 1500);
             } else {
-              await localRejudgeIfNeeded();
+              console.warn('⚠️ [ScanRecord] getApp().checkAndRecordPendingScan 不存在,无法触发补记');
             }
           } catch (e) {
-            console.warn('🚦 [traffic] 暂缓扣减复判触发失败:', e?.message || e);
+            console.warn('⚠️ [ScanRecord] merge-become 触发补记失败:', e?.message || e);
           }
           try {
-            if (typeof getApp().checkAndRecordPendingScan === 'function') {
-              await getApp().checkAndRecordPendingScan();
+            if (typeof getApp().checkTrafficDeductPending === 'function') {
+              console.log('🚦 [traffic] 触发暂缓扣减复判(merge-become 成功后)');
+              await getApp().checkTrafficDeductPending();
+            } else {
+              await localRejudgeIfNeeded();
             }
           } catch (e) {}
           if (!user.get('avatar') || user.get('nickname') == '微信用户' || !user.get('nickname')) {
@@ -338,22 +468,45 @@ Page({
       }
       return
     }
-    currentUser.set('mobile', mobile)
-    await currentUser.save()
+    try {
+      currentUser.set('mobile', mobile)
+      currentUser.set('phone', mobile)
+      await currentUser.save()
+      console.log('✅ [mobile] 已写入手机号:', {
+        userId: currentUser?.id || '无',
+        mobile: currentUser.get('mobile') || '无',
+        phone: currentUser.get('phone') || '无'
+      })
+    } catch (e) {
+      console.error('❌ [mobile] 写入手机号失败:', e?.message || e);
+      return
+    }
     try {
       wx.setStorageSync('user_mobile', mobile || '');
-      if (typeof getApp().checkTrafficDeductPending === 'function') {
-        console.log('🚦 [traffic] 触发暂缓扣减复判(绑定手机号后)');
-        await getApp().checkTrafficDeductPending();
+      try {
+        await flushPendingScanRecord(mobile);
+      } catch (e) {}
+      if (typeof getApp().checkAndRecordPendingScan === 'function') {
+        const ok = await getApp().checkAndRecordPendingScan();
+        console.log('📌 [ScanRecord] 绑定手机号后补记结果:', ok ? 'success' : 'not_written');
+        setTimeout(() => {
+          try { getApp().checkAndRecordPendingScan(); } catch (e) {}
+        }, 500);
+        setTimeout(() => {
+          try { getApp().checkAndRecordPendingScan(); } catch (e) {}
+        }, 1500);
       } else {
-        await localRejudgeIfNeeded();
+        console.warn('⚠️ [ScanRecord] getApp().checkAndRecordPendingScan 不存在,无法触发补记');
       }
     } catch (e) {
-      console.warn('🚦 [traffic] 暂缓扣减复判触发失败:', e?.message || e);
+      console.warn('⚠️ [ScanRecord] 绑定手机号后触发补记失败:', e?.message || e);
     }
     try {
-      if (typeof getApp().checkAndRecordPendingScan === 'function') {
-        await getApp().checkAndRecordPendingScan();
+      if (typeof getApp().checkTrafficDeductPending === 'function') {
+        console.log('🚦 [traffic] 触发暂缓扣减复判(绑定手机号后)');
+        await getApp().checkTrafficDeductPending();
+      } else {
+        await localRejudgeIfNeeded();
       }
     } catch (e) {}
     if (!currentUser.get('avatar') || currentUser.get('nickname') == '微信用户' || !currentUser.get('nickname')) {
@@ -637,7 +790,7 @@ Page({
         nextUrl += `&storeName=${storeName}`;
       }
 
-      wx.navigateTo({
+      wx.redirectTo({
         url: nextUrl,
         success: () => {
           console.log('✅ 跳转回 H5 页面成功');
@@ -645,8 +798,16 @@ Page({
         },
         fail: (err) => {
           console.error('❌ 跳转回 H5 页面失败:', err);
-          // 降级:正常返回上一页
-          this.normalBackLoad();
+          wx.reLaunch({
+            url: nextUrl,
+            success: () => {
+              console.log('✅ reLaunch 跳转回 H5 页面成功');
+              console.log('===========================================');
+            },
+            fail: () => {
+              this.normalBackLoad();
+            }
+          });
         }
       });
       return;
@@ -814,6 +975,11 @@ Page({
   async onComplete() {
     console.log('=== onComplete 方法被调用 ===');
     
+    if (this.data.isProcessingAuth) return;
+    this.setData({
+      isProcessingAuth: true
+    })
+
     let {
       nickname,
       avatarUrl
@@ -837,12 +1003,27 @@ Page({
     console.log('当前用户ID:', user.id);
 
     if (!finalNickname) {
+      this.setData({
+        isProcessingAuth: false
+      })
       wx.showToast({
         title: '请输入微信昵称',
         icon: 'none'
       });
       return;
     }
+
+    const hasAvatar = !!user.get('avatar')
+    if (!hasAvatar && !avatarUrl) {
+      this.setData({
+        isProcessingAuth: false
+      })
+      wx.showToast({
+        title: '请选择微信头像',
+        icon: 'none'
+      });
+      return;
+    }
     
     // 显示加载提示
     wx.showLoading({
@@ -859,6 +1040,15 @@ Page({
           console.log('✅ 头像上传成功:', avatar);
         } else {
           console.warn('⚠️ 头像上传失败');
+          wx.hideLoading();
+          this.setData({
+            isProcessingAuth: false
+          })
+          wx.showToast({
+            title: '头像上传失败,请重试',
+            icon: 'none'
+          });
+          return;
         }
       }
 
@@ -866,6 +1056,20 @@ Page({
       console.log('✅ 昵称已设置:', finalNickname);
       
       await user.save();
+      try {
+        await user.fetch();
+      } catch (e) {}
+      if (!user.get('avatar')) {
+        wx.hideLoading();
+        this.setData({
+          isProcessingAuth: false
+        })
+        wx.showToast({
+          title: '头像保存失败,请重试',
+          icon: 'none'
+        });
+        return;
+      }
       console.log('✅ 用户信息保存成功');
       
       wx.hideLoading();
@@ -887,6 +1091,9 @@ Page({
       
     } catch (err) {
       wx.hideLoading();
+      this.setData({
+        isProcessingAuth: false
+      })
       console.error('❌ 保存用户信息失败:', err);
       
       wx.showModal({
@@ -906,26 +1113,62 @@ Page({
   },
 
   //上传头像
-  updataAvatar(url) {
-    let that = this;
-    return new Promise((resolve, rejcet) => {
+  async updataAvatar(url) {
+    const normalizeUrl = (value) => {
+      if (!value) return '';
+      let s = String(value).trim();
+      if (!s) return '';
+      if (s.startsWith('//')) return 'https:' + s;
+      return s.replace(/^http:\/\//i, 'https://');
+    };
+
+    const normalizeDomain = (value) => {
+      const d = normalizeUrl(value);
+      if (!d) return '';
+      return d.endsWith('/') ? d.slice(0, -1) : d;
+    };
+
+    const downloadIfNeeded = (src) => {
+      return new Promise((resolve) => {
+        if (!src) return resolve('');
+        const isRemote = /^https?:\/\//i.test(src) || src.startsWith('//');
+        if (!isRemote) return resolve(src);
+        wx.downloadFile({
+          url: normalizeUrl(src),
+          success: (res) => resolve(res?.tempFilePath || ''),
+          fail: () => resolve('')
+        });
+      });
+    };
+
+    try {
+      if (!this.data.uptokenURL || !this.data.domain || !this.data.uploadURL) {
+        await this.getUptoken();
+      }
+    } catch (e) {}
+
+    const filePath = await downloadIfNeeded(normalizeUrl(url));
+    if (!filePath) return false;
+
+    return new Promise((resolve) => {
       qiniuUploader.upload(
-        url,
-        async (res) => {
-          let img = res.imageURL;
-          resolve(img)
+        filePath,
+        (res) => {
+          const img = normalizeUrl(res?.imageURL || res?.fileUrl || '');
+          resolve(img || false);
         },
         (error) => {
           console.log("error: " + error);
-          resolve(false)
-        }, {
-        region: "SCN",
-        uploadURL: that.data.uploadURL,
-        domain: that.data.domain,
-        uptoken: that.data.uptokenURL,
-      }
+          resolve(false);
+        },
+        {
+          region: "SCN",
+          uploadURL: this.data.uploadURL,
+          domain: normalizeDomain(this.data.domain),
+          uptoken: this.data.uptokenURL,
+        }
       );
-    })
+    });
   },
   //选择勾选用户协议
   onCheckAgreement() {

+ 7 - 1
components/diy-my/my.js

@@ -173,6 +173,12 @@ Component({
             },
         ],
     },
+    pageLifetimes: {
+        show: function () {
+            this.getUser();
+            this.getAccount();
+        }
+    },
     ready: function() {
         this.getUser();
         this.getAccount();
@@ -245,4 +251,4 @@ Component({
             });
         },
     },
-});
+});

+ 13 - 10
components/diy-personal/index.js

@@ -27,6 +27,11 @@ Component({
    async attached() {
     await this.getUser()
    },
+  pageLifetimes: {
+    show: async function () {
+      await this.getUser()
+    }
+  },
   ready: function () {
     // 在组件布局完成后执行,确保options参数中有data信息
     this.loadData();
@@ -45,16 +50,14 @@ Component({
       });
     },
     async getUser() {
-        let cuid = Parse.User.current().id
-        console.log(cuid)
-        let User = new Parse.Query('User')
-        if(cuid) {
-            let user =await User.get(cuid)
-            this.setData({
-                user: user.toJSON()
-            })
-            console.log(user)
-        }
+        const current = Parse && Parse.User && typeof Parse.User.current === 'function' ? Parse.User.current() : null
+        const cuid = current && current.id ? current.id : ''
+        if (!cuid) return
+        let User = new Parse.Query('_User')
+        let user = await User.get(cuid)
+        this.setData({
+          user: user.toJSON()
+        })
     }
   },
 });

+ 157 - 41
index.js

@@ -2,6 +2,7 @@
 // var app = getApp()
 // const { wxLogin } = require('./utils/login')
 const CONFIG = require("config.js");
+const login = require("./utils/login");
 let config = {
   appid: CONFIG.default.appid,
   company: CONFIG.default.company,
@@ -96,6 +97,13 @@ Page({
         str = str.replace(/&amp;/g, '&');
       }
       try { wx.setStorageSync('scan_raw_url', str); } catch (e) {}
+
+      if (!str.includes('?') && str.includes('=') && (str.includes('storeId=') || str.includes('storeid='))) {
+        try {
+          const obj = this.setObject(str);
+          obj && Object.keys(obj).forEach((key) => options[key] = obj[key]);
+        } catch (e) {}
+      }
       
       // 检查是否是员工邀请链接(app.fmode.cn/dev/pobingfeng/manager/staff?invite=xxx)
       if (str.includes('app.fmode.cn/dev/pobingfeng/manager/staff')) {
@@ -136,16 +144,24 @@ Page({
     // 兼容从编译参数或页面直达传入的 storeId
     if (options && options.scene && !options.storeId) {
       try {
-        const sceneStr = decodeURIComponent(options.scene)
-        if (sceneStr) {
-          const pairs = sceneStr.split('&')
+        let sceneStr = decodeURIComponent(options.scene);
+        if (sceneStr && sceneStr.indexOf('&amp;') !== -1) {
+          sceneStr = sceneStr.replace(/&amp;/g, '&');
+        }
+
+        if (sceneStr && sceneStr.includes('=')) {
+          const pairs = sceneStr.split('&');
           for (const p of pairs) {
-            const [k, v] = p.split('=')
-            if (k === 'storeId' && v) {
-              options.storeId = v
-              break
-            }
+            if (!p) continue;
+            const idx = p.indexOf('=');
+            if (idx === -1) continue;
+            const k = p.slice(0, idx);
+            const rawV = p.slice(idx + 1);
+            const v = rawV ? decodeURIComponent(rawV) : rawV;
+            if (k) options[k] = v;
           }
+        } else if (sceneStr && /^[A-Za-z0-9]{8,20}$/.test(sceneStr)) {
+          options.storeId = sceneStr;
         }
       } catch (e) {
         console.warn('解析 scene 失败:', e)
@@ -241,14 +257,24 @@ Page({
       }
 
       let currentUser = Parse.User.current()
+      const userLogin = wx.getStorageSync('userLogin');
+      const hasMobile = currentUser?.get('mobile');
+      const isFullyLoggedIn = !!(currentUser && hasMobile && userLogin);
+      const isEntryFromScan =
+        !!(options && (options.q || options.scene)) ||
+        wx.getStorageSync('storeId_from_scan') === true ||
+        wx.getStorageSync('need_scan_redirect') === true ||
+        wx.getStorageSync('need_activity_redirect') === true;
       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('userLogin 存储:', userLogin || '无');
       console.log('force 参数:', force);
+      console.log('是否完整登录:', isFullyLoggedIn);
+      console.log('是否扫码/海报入口:', isEntryFromScan);
       console.log('===========================================');
       
       // 查询 Company 的 isPublishing 字段
@@ -284,11 +310,10 @@ Page({
       }
       
       // 根据 isPublishing 决定是否强制登录
-      if (!currentUser || force) {
+      if (!isFullyLoggedIn || force) {
         console.log('🔄 开始调用 checkAuth...');
         
-        // isPublishing == true 时不强制授权,否则强制授权
-        const forceAuth = !isPublishing;
+        const forceAuth = isEntryFromScan ? true : !isPublishing;
         console.log('🔐 是否强制登录:', forceAuth);
         
         let r = await checkAuth(forceAuth);
@@ -309,6 +334,16 @@ Page({
           console.log('❌ 强制登录模式,用户未登录,停止访问');
           return;
         }
+
+        if (forceAuth) {
+          const mobile = currentUser?.get('mobile');
+          const afterLoginUserLogin = wx.getStorageSync('userLogin');
+          if (!currentUser || !mobile || !afterLoginUserLogin) {
+            wx.removeStorageSync('userLogin');
+            login.loginNow();
+            return;
+          }
+        }
         
         // 即使登录失败,也允许继续访问(仅在非强制登录模式下)
         if(!r) {
@@ -452,7 +487,32 @@ Page({
         return;
       }
       
-      // 如果出错,尝试继续跳转到主页(游客模式)
+      const isEntryFromScan =
+        !!(this.data.options && (this.data.options.q || this.data.options.scene)) ||
+        wx.getStorageSync('storeId_from_scan') === true ||
+        wx.getStorageSync('need_scan_redirect') === true ||
+        wx.getStorageSync('need_activity_redirect') === true;
+      if (isEntryFromScan) {
+        this.setData({ loading: false });
+        wx.showModal({
+          title: '需要登录',
+          content: '扫码进入需要先完成登录授权后才能继续',
+          showCancel: true,
+          cancelText: '退出',
+          confirmText: '去登录',
+          success: async (result) => {
+            if (result.confirm) {
+              try {
+                await checkAuth(true);
+              } catch (e) {}
+            } else {
+              wx.exitMiniProgram();
+            }
+          }
+        });
+        return;
+      }
+      
       console.log('⚠️ 登录出错,尝试游客模式访问');
       
       let url = getApp().globalData.rootPage || getApp().globalData.defaultTabBar.list[0].pagePath;
@@ -508,7 +568,8 @@ Page({
       const pendingScan = wx.getStorageSync('pending_scan_record');
       
       if (!pendingScan) {
-        return;
+        console.log('ℹ️ [ScanRecord] 未发现 pending_scan_record,无需补记');
+        return false;
       }
       
       console.log('===========================================');
@@ -531,7 +592,7 @@ Page({
       await this.recordUserSource(pendingScan);
       
       // 记录扫码统计
-      await this.recordScanStatistics({
+      const recordOk = await this.recordScanStatistics({
         storeId: pendingScan.storeId,
         sourceType: pendingScan.sourceType,
         sourceId: pendingScan.sourceId,
@@ -544,12 +605,17 @@ Page({
         scanTimestamp: pendingScan.timestamp
       });
       
-      // 清除待记录的扫码信息
-      wx.removeStorageSync('pending_scan_record');
-      console.log('✅ 扫码统计已记录并清除');
+      if (recordOk) {
+        wx.removeStorageSync('pending_scan_record');
+        console.log('✅ 扫码统计已记录并清除');
+      } else {
+        console.log('⏳ 扫码统计未完成写入,保留 pending_scan_record 等待后续重试');
+      }
+      return recordOk;
       
     } catch (error) {
       console.error('❌ 记录待处理扫码信息失败:', error);
+      return false;
     }
   },
 
@@ -603,6 +669,20 @@ Page({
         } catch (e) {}
         return;
       }
+
+      try {
+        const now = Date.now();
+        const lastTry = wx.getStorageSync('pending_scan_record_try_by_traffic') || 0;
+        if (!lastTry || now - lastTry > 2000) {
+          wx.setStorageSync('pending_scan_record_try_by_traffic', now);
+          if (typeof getApp().checkAndRecordPendingScan === 'function') {
+            console.log('🧾 [ScanRecord] 借助 traffic 复判触发补记');
+            const ok = await getApp().checkAndRecordPendingScan();
+            console.log('🧾 [ScanRecord] traffic 复判触发补记结果:', ok ? 'success' : 'not_written');
+          }
+        }
+      } catch (e) {}
+
       const storeId = pending && pending.storeId ? pending.storeId : null;
       if (!storeId) {
         console.warn('🚦 [traffic] 暂缓扣减任务缺少 storeId,清除任务');
@@ -1035,9 +1115,16 @@ Page({
           className: "_User",
           objectId: invite
         })
-        await Parse.Cloud.run('user_save', {
-          userJson: user.toJSON()
-        })
+        try {
+          await user.save()
+        } catch (e) {
+          try {
+            await user.fetch()
+          } catch (e2) {}
+          await Parse.Cloud.run('user_save', {
+            userJson: user.toJSON()
+          })
+        }
       }
     } catch (error) {
       console.error('❌ updateUser 失败:', error.message);
@@ -1214,8 +1301,10 @@ Page({
         console.log('📢 推广员ID (userId):', userId);
       } else {
         // 其他情况(产品码、案例码、店铺分享等)
-        sourceType = 'scan';
-        console.log('🏬 [来源识别] 来源类型: 通用扫码 (scan)');
+        sourceType = 'store';
+        sourceId = storeId;
+        console.log('🏬 [来源识别] 来源类型: 门店扫码 (store)');
+        console.log('🏬 门店ID (storeId):', storeId);
       }
       
       if (productId) {
@@ -1765,25 +1854,42 @@ Page({
       console.log('   - userId:', userId || '无');
       console.log('   - productId:', productId || '无');
       console.log('   - scanCount:', scanCount || '0');
+      console.log('🧾 [ScanRecord] 即将尝试写入 ScanRecord 表');
       
       // 检查用户是否已登录
       const currentUser = Parse.User.current();
       if (!currentUser) {
-        console.log('⚠️ [未登录] 用户未登录,不记录扫码统计');
-        return;
+        console.log('⚠️ [ScanRecord] 未写入(原因:用户未登录)');
+        return false;
       }
       
       console.log('👤 [用户信息] 当前登录用户:');
       console.log('   - 用户ID:', currentUser.id);
-      console.log('   - 手机号:', currentUser.get('mobile') || '无');
+      let storageMobile = '';
+      try { storageMobile = wx.getStorageSync('user_mobile') || ''; } catch (e) {}
+      let userMobile = currentUser.get('mobile') || '';
+      let userPhone = currentUser.get('phone') || '';
+      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);
+        if (freshUser) {
+          userMobile = freshUser.get('mobile') || userMobile;
+          userPhone = freshUser.get('phone') || userPhone;
+        }
+      } catch (e) {}
+      console.log('   - mobile:', userMobile || '无');
+      console.log('   - phone:', userPhone || '无');
+      console.log('   - storage(user_mobile):', storageMobile || '无');
       
       // 如果没有来源信息,不记录统计
       if (!sourceId && !ownerId && !employeeId && !partnerId && !userId) {
-        console.log('ℹ️ [跳过] 无来源信息,跳过统计记录');
-        return;
+        console.log('ℹ️ [ScanRecord] 未写入(原因:无来源信息)');
+        return false;
       }
 
-      const hasMobile = !!currentUser.get('mobile');
+      const hasMobile = !!normalizeCnMobile(userMobile || userPhone || storageMobile);
       const scanTs = typeof scanTimestamp === 'number' && scanTimestamp > 0 ? scanTimestamp : Date.now();
 
       if (partnerId && !hasMobile) {
@@ -1809,8 +1915,8 @@ Page({
             wx.setStorageSync('pending_scan_record', pendingData);
           } catch (e2) {}
         }
-        console.log('⏳ [异业扫码] 用户未绑定手机号,延后到注册登录后再统计');
-        return;
+        console.log('⏳ [ScanRecord] 未写入(原因:异业扫码且未绑定手机号,已暂存 pending_scan_record)');
+        return false;
       }
       
       console.log('✅ [开始记录] 用户已登录且有来源信息,开始记录');
@@ -1837,10 +1943,6 @@ Page({
       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);
@@ -1864,11 +1966,16 @@ Page({
         className: '_User',
         objectId: currentUser.id
       });
+      record.set('userid', currentUser.id);
       console.log('   - user:', currentUser.id);
       
       await record.save();
-      console.log('✅ [保存成功] 扫码记录已保存到数据库');
-      console.log('   - 记录ID:', record.id);
+      console.log('✅ [ScanRecord] 保存成功');
+      console.log('   - recordId:', record.id);
+      console.log('   - userId:', currentUser.id);
+      console.log('   - storeId:', storeId);
+      console.log('   - sourceType:', sourceType || 'unknown');
+      console.log('   - sourceId:', sourceId || '无');
 
       // 如果存在异业合作伙伴ID,处理异业绑定和扫码次数
       if (partnerId) {
@@ -1892,7 +1999,7 @@ Page({
             console.log('   - 已绑定异业:', userBoundPartnerId);
             console.log('   - 当前扫码异业:', partnerId);
             console.log('🤝 ===========================================');
-            return;
+            return true;
           }
           
           // 规则2:检查该用户在该门店是否已经扫过该异业的码
@@ -1915,7 +2022,7 @@ Page({
             console.log('   - 计入时间:', existingScan.get('scanTime'));
             console.log('   - 记录ID:', existingScan.id);
             console.log('🤝 ===========================================');
-            return;
+            return true;
           }
           
           let partnerNewUserQualified = false;
@@ -1930,7 +2037,7 @@ Page({
           if (!partnerNewUserQualified) {
             console.log('ℹ️ [规则2] 非“扫码注册登录的新用户”,不计入异业扫码次数');
             console.log('🤝 ===========================================');
-            return;
+            return true;
           }
 
           record.set('partnerNewUserCounted', true);
@@ -1998,9 +2105,18 @@ Page({
           console.log('🤝 ===========================================');
         }
       }
+      return true;
     } catch (error) {
-      console.warn('⚠️ 保存扫码记录失败:', error);
+      console.error('❌ [ScanRecord] 保存失败');
+      console.error('   - message:', error?.message || error);
+      if (error && typeof error === 'object') {
+        try {
+          console.error('   - code:', error.code);
+          console.error('   - details:', error.details);
+        } catch (e) {}
+      }
       // 不影响主流程
+      return false;
     }
   }
 });

+ 33 - 1
nova-pbf/pages/index/index.js

@@ -67,7 +67,9 @@ Page({
       console.error('设置店铺 ID 失败:', e);
     }
 
-    // ✅ 立即加载并设置店铺名称作为页面标题
+    const ok = await this.ensureLoggedIn();
+    if (!ok) return;
+
     await this.loadAndSetStoreTitle()
   },
 
@@ -82,6 +84,11 @@ Page({
    * 生命周期函数--监听页面显示
    */
   onShow: function () {
+    this.ensureLoggedIn().then((ok) => {
+      if (ok) {
+        this.loadAndSetStoreTitle();
+      }
+    });
   },
   /**
    * 生命周期函数--监听页面隐藏
@@ -173,4 +180,29 @@ Page({
       console.error('❌ 加载店铺信息失败:', error);
     }
   }
+  ,
+
+  ensureLoggedIn: async function () {
+    try {
+      const currentUser = Parse.User.current();
+      const hasMobile = currentUser?.get('mobile');
+      const userLogin = wx.getStorageSync('userLogin');
+      if (currentUser && hasMobile && userLogin) {
+        return true;
+      }
+
+      const ok = await getApp().checkAuth(true);
+      const nextUser = Parse.User.current();
+      if (ok && nextUser && nextUser.get('mobile')) {
+        wx.setStorageSync('userLogin', nextUser.id);
+        return true;
+      }
+
+      login.loginNow();
+      return false;
+    } catch (e) {
+      login.loginNow();
+      return false;
+    }
+  }
 })