|
|
@@ -90,12 +90,12 @@ Page({
|
|
|
// 处理扫码链接(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')) {
|
|
|
@@ -194,6 +194,10 @@ Page({
|
|
|
} catch (e) {
|
|
|
console.warn('🚦 [traffic] 注册全局复判方法失败:', e?.message || e);
|
|
|
}
|
|
|
+
|
|
|
+ try {
|
|
|
+ getApp().checkAndRecordPendingScan = this.checkAndRecordPendingScan.bind(this);
|
|
|
+ } catch (e) {}
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
@@ -536,7 +540,8 @@ Page({
|
|
|
partnerId: pendingScan.partnerId,
|
|
|
userId: pendingScan.userId,
|
|
|
productId: pendingScan.productId,
|
|
|
- scanCount: pendingScan.scanCount
|
|
|
+ scanCount: pendingScan.scanCount,
|
|
|
+ scanTimestamp: pendingScan.timestamp
|
|
|
});
|
|
|
|
|
|
// 清除待记录的扫码信息
|
|
|
@@ -690,8 +695,76 @@ Page({
|
|
|
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) {
|
|
|
+ if (!existingSource || shouldOverride) {
|
|
|
+ if (existingSource && shouldOverride) {
|
|
|
+ console.log('🔁 [来源升级] 触发覆盖: 从', oldType, '→', newType);
|
|
|
+ }
|
|
|
let sourceInfo = {
|
|
|
type: 'self_entry',
|
|
|
label: '自主进入',
|
|
|
@@ -757,9 +830,19 @@ Page({
|
|
|
console.log('🔍 [来源类型] 员工');
|
|
|
|
|
|
try {
|
|
|
- const employeeQuery = new Parse.Query('Employee');
|
|
|
- const employee = await employeeQuery.get(employeeId);
|
|
|
- const employeeName = employee.get('name') || '未知员工';
|
|
|
+ // 兼容管理员端员工表 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',
|
|
|
@@ -1301,7 +1384,8 @@ Page({
|
|
|
partnerId,
|
|
|
userId,
|
|
|
productId,
|
|
|
- scanCount
|
|
|
+ scanCount,
|
|
|
+ scanTimestamp: Date.now()
|
|
|
});
|
|
|
} else {
|
|
|
// 用户未登录,保存扫码信息,等登录后再记录
|
|
|
@@ -1667,7 +1751,8 @@ Page({
|
|
|
partnerId,
|
|
|
userId,
|
|
|
productId,
|
|
|
- scanCount
|
|
|
+ scanCount,
|
|
|
+ scanTimestamp
|
|
|
} = params;
|
|
|
|
|
|
console.log('📋 [统计参数] 接收到的参数:');
|
|
|
@@ -1697,6 +1782,36 @@ Page({
|
|
|
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('✅ [开始记录] 用户已登录且有来源信息,开始记录');
|
|
|
|
|
|
@@ -1791,18 +1906,35 @@ Page({
|
|
|
});
|
|
|
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('⚠️ [规则2] 该用户在该门店已计入“异业新用户扫码”,不重复计数');
|
|
|
+ console.log(' - 计入时间:', existingScan.get('scanTime'));
|
|
|
console.log(' - 记录ID:', existingScan.id);
|
|
|
console.log('🤝 ===========================================');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- console.log('✅ [首次扫码] 该用户在该门店首次扫该异业码');
|
|
|
+ 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) {
|
|
|
@@ -1853,6 +1985,10 @@ Page({
|
|
|
} else {
|
|
|
console.error('❌ [错误] 未找到异业合作伙伴记录');
|
|
|
}
|
|
|
+
|
|
|
+ if (record && record.get && record.get('partnerNewUserCounted') === true) {
|
|
|
+ await record.save();
|
|
|
+ }
|
|
|
|
|
|
console.log('🤝 ===========================================');
|
|
|
} catch (e) {
|