fix scan routing and zip download errors

This commit is contained in:
汤学会 2026-07-10 10:12:03 +08:00
parent 28b8dc3773
commit be55bd44fe
5 changed files with 190 additions and 30 deletions

View File

@ -381,9 +381,14 @@ Page({
await this.shareZipFile(filePath, fileName) await this.shareZipFile(filePath, fileName)
} catch (error) { } catch (error) {
wx.hideLoading() wx.hideLoading()
const message = error && error.errMsg && error.errMsg.includes('cancel') let message = error.message || '转发失败'
? '已取消转发' if (error && error.errMsg && error.errMsg.includes('cancel')) {
: (error.message || '转发失败') message = '已取消转发'
} else if (error && error.statusCode === 404) {
message = 'ZIP文件不存在或已过期请重新生成'
} else if (error && error.statusCode === 409) {
message = 'ZIP还未生成完成请刷新后重试'
}
wx.showToast({ title: message, icon: 'none' }) wx.showToast({ title: message, icon: 'none' })
} }
}, },

View File

@ -1,5 +1,6 @@
const api = require('../../utils/api') const api = require('../../utils/api')
const locationGuard = require('../../utils/locationGuard') const locationGuard = require('../../utils/locationGuard')
const { buildErpLoginConfirmUrlFromScanResult } = require('../../utils/scanRouting')
const avatarKey = phone => `jh_wrs_avatar_${phone}` const avatarKey = phone => `jh_wrs_avatar_${phone}`
@ -373,22 +374,14 @@ Page({
return raw return raw
}, },
async scanDevice() { async scanDevice() {
wx.showLoading({ title: '定位中' })
try {
await locationGuard.ensureFactoryLocation()
} catch (error) {
wx.hideLoading()
wx.showModal({
title: '无法扫码报工',
content: error.message || '当前位置不在允许范围内',
showCancel: false,
})
return
}
wx.hideLoading()
wx.scanCode({ wx.scanCode({
onlyFromCamera: false, onlyFromCamera: false,
success: async res => { success: async res => {
const erpLoginUrl = buildErpLoginConfirmUrlFromScanResult(res)
if (erpLoginUrl) {
wx.navigateTo({ url: erpLoginUrl })
return
}
const candidates = [res.path, res.result, res.rawData] const candidates = [res.path, res.result, res.rawData]
const moldName = candidates.reduce((found, item) => ( const moldName = candidates.reduce((found, item) => (
found || this.extractMoldName(item) found || this.extractMoldName(item)
@ -398,21 +391,23 @@ Page({
return return
} }
const paramName = moldName.startsWith('mold=') ? 'scene' : 'moldName' const paramName = moldName.startsWith('mold=') ? 'scene' : 'moldName'
if (paramName === 'scene') { try {
try { wx.showLoading({ title: paramName === 'scene' ? '校验中' : '定位中' })
wx.showLoading({ title: '校验中' }) if (paramName === 'scene') {
const resolved = await api.resolveMoldScene(moldName) const resolved = await api.resolveMoldScene(moldName)
await locationGuard.ensureFactoryLocation(resolved.attendancePointName) await locationGuard.ensureFactoryLocation(resolved.attendancePointName)
} catch (error) { } else {
wx.showModal({ await locationGuard.ensureFactoryLocation()
title: '无法扫码报工',
content: error.message || '当前位置不在该考勤点范围内',
showCancel: false,
})
return
} finally {
wx.hideLoading()
} }
} catch (error) {
wx.showModal({
title: '无法扫码报工',
content: error.message || '当前位置不在允许范围内',
showCancel: false,
})
return
} finally {
wx.hideLoading()
} }
wx.navigateTo({ wx.navigateTo({
url: `/pages/clock/clock?${paramName}=${encodeURIComponent(moldName)}`, url: `/pages/clock/clock?${paramName}=${encodeURIComponent(moldName)}`,

View File

@ -0,0 +1,54 @@
import assert from "node:assert/strict";
import { createRequire } from "node:module";
import { existsSync, readFileSync } from "node:fs";
const require = createRequire(import.meta.url);
const indexSource = readFileSync("pages/index/index.js", "utf8");
const helperPath = "utils/scanRouting.js";
assert.ok(existsSync(helperPath), "scan routing helper should exist");
const { buildErpLoginConfirmUrlFromScanResult } = require("../utils/scanRouting.js");
assert.equal(
buildErpLoginConfirmUrlFromScanResult({
path: "pages/erpLoginConfirm/erpLoginConfirm?scene=t%3Dabc123%26s%3D7",
}),
"/pages/erpLoginConfirm/erpLoginConfirm?scene=t%3Dabc123%26s%3D7",
);
assert.equal(
buildErpLoginConfirmUrlFromScanResult({
result: "scene=t%3DcompactTicket%26s%3D9",
}),
"/pages/erpLoginConfirm/erpLoginConfirm?scene=t%3DcompactTicket%26s%3D9",
);
assert.equal(
buildErpLoginConfirmUrlFromScanResult({
path: "pages/erpLoginConfirm/erpLoginConfirm?ticket=legacyTicket&session_id=12",
}),
"/pages/erpLoginConfirm/erpLoginConfirm?scene=t%3DlegacyTicket%26s%3D12",
);
assert.equal(
buildErpLoginConfirmUrlFromScanResult({
result: "pages/clock/clock?scene=mold%3Dabc123",
}),
"",
);
assert.equal(
buildErpLoginConfirmUrlFromScanResult({
result: "mold=abc123",
}),
"",
);
const scanDeviceIndex = indexSource.indexOf("async scanDevice()");
const erpRoutingIndex = indexSource.indexOf("buildErpLoginConfirmUrlFromScanResult", scanDeviceIndex);
const moldRoutingIndex = indexSource.indexOf("extractMoldName", scanDeviceIndex);
const locationGuardIndex = indexSource.indexOf("ensureFactoryLocation", scanDeviceIndex);
assert.ok(scanDeviceIndex >= 0, "index page should have scanDevice");
assert.ok(erpRoutingIndex > scanDeviceIndex, "scanDevice should check ERP login scan result");
assert.ok(erpRoutingIndex < moldRoutingIndex, "ERP login scan should be routed before mold parsing");
assert.ok(erpRoutingIndex < locationGuardIndex, "ERP login scan should not require report location guard");
console.log("erp login scan routing checks passed");

View File

@ -111,7 +111,9 @@ const download = options => new Promise((resolve, reject) => {
if (res.statusCode === 401) { if (res.statusCode === 401) {
clearAuth() clearAuth()
} }
reject(new Error(`下载失败:${res.statusCode}`)) const error = new Error(`下载失败:${res.statusCode}`)
error.statusCode = res.statusCode
reject(error)
}, },
fail: err => { fail: err => {
reject(new Error(err.errMsg || '下载失败')) reject(new Error(err.errMsg || '下载失败'))
@ -137,7 +139,9 @@ const downloadUrl = options => new Promise((resolve, reject) => {
if (res.statusCode === 401) { if (res.statusCode === 401) {
clearAuth() clearAuth()
} }
reject(new Error(`下载失败:${res.statusCode}`)) const error = new Error(`下载失败:${res.statusCode}`)
error.statusCode = res.statusCode
reject(error)
}, },
fail: err => { fail: err => {
reject(new Error(err.errMsg || '下载失败')) reject(new Error(err.errMsg || '下载失败'))

102
utils/scanRouting.js Normal file
View File

@ -0,0 +1,102 @@
const ERP_LOGIN_PAGE = '/pages/erpLoginConfirm/erpLoginConfirm'
const ERP_LOGIN_PAGE_PATH = 'pages/erpLoginConfirm/erpLoginConfirm'
const decodeRepeated = value => {
let text = String(value || '').trim()
for (let index = 0; index < 3; index += 1) {
try {
const decoded = decodeURIComponent(text)
if (decoded === text) {
break
}
text = decoded
} catch (error) {
break
}
}
return text
}
const extractParam = (value, names) => {
const raw = decodeRepeated(value)
for (let index = 0; index < names.length; index += 1) {
const name = names[index]
const matched = raw.match(new RegExp(`(?:^|[?&])${name}=([^&]+)`))
if (matched && matched[1]) {
return decodeRepeated(matched[1])
}
}
return ''
}
const extractRawParam = (value, names) => {
const raw = String(value || '').trim()
for (let index = 0; index < names.length; index += 1) {
const name = names[index]
const matched = raw.match(new RegExp(`(?:^|[?&])${name}=([^&]+)`))
if (matched && matched[1]) {
return matched[1]
}
}
return ''
}
const erpSceneFromText = value => {
const rawText = String(value || '').trim()
const rawScene = extractRawParam(rawText, ['scene'])
if (rawScene) {
const decodedScene = decodeRepeated(rawScene)
const nested = erpSceneFromText(decodedScene)
if (nested) {
return nested
}
const sceneTicket = extractParam(decodedScene, ['ticket', 't'])
const sessionId = extractParam(rawText, ['session_id', 'sessionId', 's'])
if (sceneTicket && sessionId) {
return `t=${sceneTicket}&s=${sessionId}`
}
}
const raw = decodeRepeated(value)
if (!raw) {
return ''
}
const nestedScene = extractParam(raw, ['scene'])
if (nestedScene) {
const nested = erpSceneFromText(nestedScene)
if (nested) {
return nested
}
}
const ticket = extractParam(raw, ['ticket', 't'])
const sessionId = extractParam(raw, ['session_id', 'sessionId', 's'])
if (!ticket || !sessionId) {
return ''
}
return `t=${ticket}&s=${sessionId}`
}
const isErpLoginPath = value => decodeRepeated(value).replace(/^\/+/, '').includes(ERP_LOGIN_PAGE_PATH)
const buildErpLoginConfirmUrlFromScanResult = scanResult => {
const candidates = [
scanResult && scanResult.path,
scanResult && scanResult.result,
scanResult && scanResult.rawData,
]
for (let index = 0; index < candidates.length; index += 1) {
const candidate = decodeRepeated(candidates[index])
if (!candidate) {
continue
}
const scene = erpSceneFromText(candidate)
if (scene && (isErpLoginPath(candidate) || !extractParam(scene, ['mold']))) {
return `${ERP_LOGIN_PAGE}?scene=${encodeURIComponent(scene)}`
}
}
return ''
}
module.exports = {
buildErpLoginConfirmUrlFromScanResult,
decodeRepeated,
}