cut / BeadQR /miniprogram /utils /security.js
Ethscriptions's picture
上传 BeadQR 微信小程序项目源码
9de7b70 verified
Raw History Blame Contribute Delete
4.93 kB
// 内容安全链路(微信平台规范)
// 文本:security.msgSecCheck v2(二维码内容 / 扫码导入内容 / 水印)
// 图片:先按官方建议压缩到 750×1334 内、1MB 内的 JPG,再上传云存储由云函数调用 security.imgSecCheck
// 约定返回:{ status: 'pass' | 'risky' | 'error' }
// - risky:命中违规,必须拦截
// - error:检测服务不可用,同样拦截(禁止静默放行)
// 通过结果按内容缓存,导出时无需重复请求,显著提升导出速度
const FUNCTION_NAME = 'quickstartFunctions'
const MAX_W = 750
const MAX_H = 1334
const textCache = new Map() // value -> 'pass' | 'risky'
const pending = new Map() // value -> Promise
function callFunction(data) {
return new Promise((resolve) => {
if (!wx.cloud) { resolve(null); return }
wx.cloud.callFunction({
name: FUNCTION_NAME,
data,
success: (res) => resolve((res && res.result) || null),
fail: () => resolve(null),
})
})
}
function normalize(result) {
if (!result) return 'error'
if (result.risk) return 'risky'
if (result.skipped) return 'error'
return 'pass'
}
function checkText(content, scene = 2) {
const value = String(content || '').trim().slice(0, 2500)
if (!value) return Promise.resolve({ status: 'pass' })
const cached = textCache.get(value)
if (cached) return Promise.resolve({ status: cached, cached: true })
if (pending.has(value)) return pending.get(value)
const task = callFunction({ type: 'checkMessage', content: value, scene }).then((result) => {
const status = normalize(result)
if (status !== 'error') textCache.set(value, status)
pending.delete(value)
return { status }
})
pending.set(value, task)
return task
}
// 导出前一次性校验多段文本(已缓存的直接复用,未缓存的合并为一次云函数调用)
function checkTexts(items) {
const list = items.map((it) => ({ value: String(it.value || '').trim().slice(0, 2500), scene: it.scene || 2 })).filter((it) => it.value)
const results = list.map((it) => textCache.get(it.value))
if (results.includes('risky')) return Promise.resolve({ status: 'risky' })
const missing = list.filter((it, i) => !results[i])
if (!missing.length) return Promise.resolve({ status: 'pass' })
return callFunction({ type: 'checkMessages', items: missing }).then((res) => {
if (!res || !Array.isArray(res.results)) return { status: 'error' }
let status = 'pass'
res.results.forEach((r, i) => {
const s = normalize(r)
if (s !== 'error') textCache.set(missing[i].value, s)
if (s === 'risky') status = 'risky'
else if (s === 'error' && status === 'pass') status = 'error'
})
return { status }
})
}
function isTextPassed(value) {
const v = String(value || '').trim()
return !v || textCache.get(v) === 'pass'
}
// 按官方尺寸建议压缩(同时加快上传)
function compressForCheck(canvas, image, path) {
return new Promise((resolve) => {
if (!canvas || !image) { resolve(path); return }
try {
const w0 = image.width || image._w
const h0 = image.height || image._h
const scale = Math.min(1, MAX_W / w0, MAX_H / h0)
canvas.width = Math.max(1, Math.round(w0 * scale))
canvas.height = Math.max(1, Math.round(h0 * scale))
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#ffffff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
wx.canvasToTempFilePath({
canvas,
fileType: 'jpg',
quality: 0.82,
destWidth: canvas.width,
destHeight: canvas.height,
success: (res) => resolve(res.tempFilePath),
fail: () => resolve(path),
})
} catch (e) {
resolve(path)
}
})
}
function upload(filePath) {
return new Promise((resolve) => {
if (!wx.cloud) { resolve(''); return }
wx.cloud.uploadFile({
cloudPath: `sec-check/${Date.now()}-${Math.random().toString(36).slice(2, 10)}.jpg`,
filePath,
success: (res) => resolve((res && res.fileID) || ''),
fail: () => resolve(''),
})
})
}
// image:已加载的 CanvasImage(用于压缩);path:本地临时路径
function checkImage(path, image, canvas) {
if (!path) return Promise.resolve({ status: 'error' })
return compressForCheck(canvas, image, path)
.then(upload)
.then((fileID) => {
if (!fileID) return { status: 'error' }
// 云函数检测完毕后会立即删除该临时文件
return callFunction({ type: 'checkImage', fileID }).then((result) => ({ status: normalize(result) }))
})
.catch(() => ({ status: 'error' }))
}
const MESSAGES = {
risky: '内容含违规信息,请修改后再试',
error: '安全检测暂不可用,请检查网络后重试',
}
module.exports = { checkText, checkTexts, isTextPassed, checkImage, MESSAGES }