Showing
1 changed file
with
230 additions
and
0 deletions
doc/针对弱网 / 无网场景的扫码核销.md
0 → 100644
| 1 | +2. 关键功能模块实现(微信小程序示例) | ||
| 2 | +以下是核心代码实现,基于微信小程序的 wx.getNetworkType 检测网络、wx.setStorageSync 本地存储、自有后端同步的方案: | ||
| 3 | +```javascript | ||
| 4 | +// pages/verify/verify.js | ||
| 5 | +// 下面是主要思路和实现, 需要修改的是扫描成功之后需要跳转到另一个页面显示核销成功/失败的提示 | ||
| 6 | +// 1. 调用 scanCodeVerify 方法开始扫码核销 | ||
| 7 | +// 2. 扫码成功后,调用 handleVerify 方法处理核销逻辑 | ||
| 8 | +// 3. 在 handleVerify 方法中,先检测网络状态 | ||
| 9 | +// 4. 如果有网,实时校验核销码并调用后端核销接口 | ||
| 10 | +// 5. 如果无网,先校验本地存储中是否有该核销码的记录 | ||
| 11 | +// 6. 如果有记录,说明之前核销过,提示用户已核销 | ||
| 12 | +// 7. 如果无记录,提示用户无网络,无法核销 | ||
| 13 | +// 8. 核销成功后,同步更新本地记录(可选) | ||
| 14 | +Page({ | ||
| 15 | + /** | ||
| 16 | + * 扫码核销核心方法 | ||
| 17 | + */ | ||
| 18 | + scanCodeVerify() { | ||
| 19 | + // 1. 调起微信扫码API | ||
| 20 | + wx.scanCode({ | ||
| 21 | + onlyFromCamera: true, // 仅允许从相机扫码 | ||
| 22 | + scanType: ['qrCode'], // 仅识别二维码 | ||
| 23 | + success: (res) => { | ||
| 24 | + const verifyCode = res.result; // 扫码获取的核销码 | ||
| 25 | + this.handleVerify(verifyCode); | ||
| 26 | + }, | ||
| 27 | + fail: (err) => { | ||
| 28 | + wx.showToast({ title: '扫码失败,请重试', icon: 'none' }); | ||
| 29 | + console.error('扫码失败:', err); | ||
| 30 | + } | ||
| 31 | + }); | ||
| 32 | + }, | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 处理核销逻辑(核心) | ||
| 36 | + * @param {string} verifyCode 核销码 | ||
| 37 | + */ | ||
| 38 | + handleVerify(verifyCode) { | ||
| 39 | + wx.showLoading({ title: '核销中...' }); | ||
| 40 | + | ||
| 41 | + // 第一步:检测网络状态 | ||
| 42 | + wx.getNetworkType({ | ||
| 43 | + success: (networkRes) => { | ||
| 44 | + const networkType = networkRes.networkType; | ||
| 45 | + // 判断是否有网(wifi/4g/5g为有网,none/unknown为无网) | ||
| 46 | + if (['wifi', '4g', '5g', '3g', '2g'].includes(networkType)) { | ||
| 47 | + // 有网场景:实时校验+核销 | ||
| 48 | + this.verifyOnline(verifyCode); | ||
| 49 | + } else { | ||
| 50 | + // 无网场景:本地校验+离线核销 | ||
| 51 | + this.verifyOffline(verifyCode); | ||
| 52 | + } | ||
| 53 | + }, | ||
| 54 | + fail: () => { | ||
| 55 | + // 网络检测失败,默认走离线逻辑 | ||
| 56 | + this.verifyOffline(verifyCode); | ||
| 57 | + wx.hideLoading(); | ||
| 58 | + } | ||
| 59 | + }); | ||
| 60 | + }, | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * 有网核销逻辑 | ||
| 64 | + * @param {string} verifyCode 核销码 | ||
| 65 | + */ | ||
| 66 | + verifyOnline(verifyCode) { | ||
| 67 | + wx.request({ | ||
| 68 | + url: 'https://你的服务器域名/api/verify', // 后端核销接口 | ||
| 69 | + method: 'POST', | ||
| 70 | + data: { verifyCode }, | ||
| 71 | + success: (res) => { | ||
| 72 | + wx.hideLoading(); | ||
| 73 | + if (res.data.code === 200) { | ||
| 74 | + wx.showToast({ title: '核销成功', icon: 'success' }); | ||
| 75 | + // 同步本地预约状态(可选) | ||
| 76 | + this.updateLocalVerifyStatus(verifyCode, true); | ||
| 77 | + } else { | ||
| 78 | + wx.showToast({ title: res.data.msg || '核销失败', icon: 'none' }); | ||
| 79 | + } | ||
| 80 | + }, | ||
| 81 | + fail: (err) => { | ||
| 82 | + wx.hideLoading(); | ||
| 83 | + // 网络请求失败,降级到离线核销 | ||
| 84 | + wx.showToast({ title: '网络不稳定,将使用离线核销', icon: 'none' }); | ||
| 85 | + setTimeout(() => { | ||
| 86 | + this.verifyOffline(verifyCode); | ||
| 87 | + }, 1500); | ||
| 88 | + console.error('在线核销失败:', err); | ||
| 89 | + } | ||
| 90 | + }); | ||
| 91 | + }, | ||
| 92 | + | ||
| 93 | + /** | ||
| 94 | + * 无网核销逻辑 | ||
| 95 | + * @param {string} verifyCode 核销码 | ||
| 96 | + */ | ||
| 97 | + verifyOffline(verifyCode) { | ||
| 98 | + wx.hideLoading(); | ||
| 99 | + // 1. 读取本地缓存的预约数据(需提前同步到本地) | ||
| 100 | + const localAppointments = wx.getStorageSync('localAppointments') || []; | ||
| 101 | + // 2. 本地校验核销码是否有效 | ||
| 102 | + const targetAppointment = localAppointments.find(item => item.verifyCode === verifyCode); | ||
| 103 | + | ||
| 104 | + if (!targetAppointment) { | ||
| 105 | + wx.showToast({ title: '未找到该预约记录', icon: 'none' }); | ||
| 106 | + return; | ||
| 107 | + } | ||
| 108 | + if (targetAppointment.isVerified) { | ||
| 109 | + wx.showToast({ title: '该预约已核销', icon: 'none' }); | ||
| 110 | + return; | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + // 3. 本地核销:更新本地状态+记录离线操作 | ||
| 114 | + targetAppointment.isVerified = true; | ||
| 115 | + const offlineVerifyRecords = wx.getStorageSync('offlineVerifyRecords') || []; | ||
| 116 | + offlineVerifyRecords.push({ | ||
| 117 | + verifyCode, | ||
| 118 | + appointmentId: targetAppointment.id, | ||
| 119 | + verifyTime: new Date().getTime(), // 核销时间戳 | ||
| 120 | + status: 'pending' // 待同步 | ||
| 121 | + }); | ||
| 122 | + | ||
| 123 | + // 4. 保存本地修改 | ||
| 124 | + wx.setStorageSync('localAppointments', localAppointments); | ||
| 125 | + wx.setStorageSync('offlineVerifyRecords', offlineVerifyRecords); | ||
| 126 | + | ||
| 127 | + wx.showToast({ title: '离线核销成功,网络恢复后自动同步', icon: 'success' }); | ||
| 128 | + }, | ||
| 129 | + | ||
| 130 | + /** | ||
| 131 | + * 网络恢复后同步离线核销记录 | ||
| 132 | + */ | ||
| 133 | + syncOfflineRecords() { | ||
| 134 | + // 监听网络状态变化 | ||
| 135 | + wx.onNetworkStatusChange((res) => { | ||
| 136 | + if (res.isConnected) { | ||
| 137 | + const offlineRecords = wx.getStorageSync('offlineVerifyRecords') || []; | ||
| 138 | + if (offlineRecords.length === 0) return; | ||
| 139 | + | ||
| 140 | + // 批量同步离线记录到服务器 | ||
| 141 | + wx.request({ | ||
| 142 | + url: 'https://你的服务器域名/api/syncOfflineVerify', | ||
| 143 | + method: 'POST', | ||
| 144 | + data: { records: offlineRecords }, | ||
| 145 | + success: (res) => { | ||
| 146 | + if (res.data.code === 200) { | ||
| 147 | + // 同步成功:清空离线记录 | ||
| 148 | + wx.setStorageSync('offlineVerifyRecords', []); | ||
| 149 | + wx.showToast({ title: '离线核销记录已同步', icon: 'success' }); | ||
| 150 | + } | ||
| 151 | + }, | ||
| 152 | + fail: (err) => { | ||
| 153 | + console.error('离线记录同步失败:', err); | ||
| 154 | + } | ||
| 155 | + }); | ||
| 156 | + } | ||
| 157 | + }); | ||
| 158 | + }, | ||
| 159 | + | ||
| 160 | + /** | ||
| 161 | + * 提前同步预约数据到本地(页面加载时执行) | ||
| 162 | + */ | ||
| 163 | + syncAppointmentsToLocal() { | ||
| 164 | + // 页面初始化/小程序启动时,拉取预约数据到本地 | ||
| 165 | + wx.request({ | ||
| 166 | + url: 'https://你的服务器域名/api/getAppointments', | ||
| 167 | + success: (res) => { | ||
| 168 | + if (res.data.code === 200) { | ||
| 169 | + wx.setStorageSync('localAppointments', res.data.data); | ||
| 170 | + } | ||
| 171 | + }, | ||
| 172 | + fail: () => { | ||
| 173 | + // 网络失败则使用上次缓存的本地数据 | ||
| 174 | + console.log('使用本地缓存的预约数据'); | ||
| 175 | + } | ||
| 176 | + }); | ||
| 177 | + }, | ||
| 178 | + | ||
| 179 | + /** | ||
| 180 | + * 更新本地核销状态(辅助方法) | ||
| 181 | + */ | ||
| 182 | + updateLocalVerifyStatus(verifyCode, isVerified) { | ||
| 183 | + const localAppointments = wx.getStorageSync('localAppointments') || []; | ||
| 184 | + const index = localAppointments.findIndex(item => item.verifyCode === verifyCode); | ||
| 185 | + if (index > -1) { | ||
| 186 | + localAppointments[index].isVerified = isVerified; | ||
| 187 | + wx.setStorageSync('localAppointments', localAppointments); | ||
| 188 | + } | ||
| 189 | + }, | ||
| 190 | + | ||
| 191 | + /** | ||
| 192 | + * 页面加载时初始化 | ||
| 193 | + */ | ||
| 194 | + onLoad(options) { | ||
| 195 | + // 1. 同步预约数据到本地 | ||
| 196 | + this.syncAppointmentsToLocal(); | ||
| 197 | + // 2. 监听网络状态,同步离线记录 | ||
| 198 | + this.syncOfflineRecords(); | ||
| 199 | + // 3. 主动检查一次是否有未同步的离线记录 | ||
| 200 | + wx.getNetworkType({ | ||
| 201 | + success: (res) => { | ||
| 202 | + if (['wifi', '4g', '5g'].includes(res.networkType)) { | ||
| 203 | + this.syncOfflineRecords(); | ||
| 204 | + } | ||
| 205 | + } | ||
| 206 | + }); | ||
| 207 | + } | ||
| 208 | +}); | ||
| 209 | +``` | ||
| 210 | +3. 配套设计要点 | ||
| 211 | +- 本地数据预处理 | ||
| 212 | + - 小程序启动 / 核销页面加载时,主动拉取当前门店 / 账号下的待核销预约数据,缓存到本地(wx.setStorageSync 或 wx.setStorage); | ||
| 213 | + - 本地数据需包含:预约 ID、核销码、用户信息、预约时间、核销状态等核心字段,避免冗余。 | ||
| 214 | +- 防重复核销设计 | ||
| 215 | + - 本地记录核销码的核销状态(isVerified),即使无网也能避免重复核销; | ||
| 216 | + - 服务器端需做最终校验,同步离线记录时若发现已核销,返回提示并更新本地状态。 | ||
| 217 | +- 数据安全与容错 | ||
| 218 | + - 本地存储的核销记录需加简单加密(如 base64 + 时间戳),防止篡改; | ||
| 219 | + - 离线记录同步时,服务器需校验核销码有效性、时间范围(如预约有效期),避免无效核销; | ||
| 220 | + - 小程序退出 / 重启后,保留本地缓存(微信小程序本地缓存默认长期有效,可设置过期时间)。 | ||
| 221 | +- 用户体验优化 | ||
| 222 | + - 无网核销时,明确提示 “离线核销成功,网络恢复后自动同步”; | ||
| 223 | + - 网络恢复后自动同步,无需用户手动操作; | ||
| 224 | + - 提供 “离线记录查询” 入口,方便查看未同步的核销记录。 | ||
| 225 | + | ||
| 226 | + | ||
| 227 | +总结 | ||
| 228 | + 核心逻辑:无网时依赖本地缓存的预约数据完成核销,记录离线操作;有网时实时核销,网络恢复后自动同步离线记录; | ||
| 229 | + 关键保障:提前同步待核销数据到本地、本地标记核销状态防重复、服务器端做最终校验; | ||
| 230 | + 体验优化:明确的网络状态提示、自动同步机制,减少用户感知网络差异。 |
-
Please register or login to post a comment