/** * Soul创业派对 - 小程序码 scene 参数统一编解码(海报生成 ↔ 扫码解析闭环) * 官方以 options.scene 接收扫码参数;后端生成码时会把 & 转为 _,故解析时同时支持 & 和 _ * scene 同时可带两个参数:章节标识(mid/id) + 推荐人(ref) */ const SEP = '_' // 生成时统一用 _,与微信实际存储一致,且不占 32 字符限制 /** * 编码:生成海报/分享时组 scene 字符串(同时带 mid或id + ref) * @param {{ mid?: number, id?: string, ref?: string }} opts * @returns {string} 如 "mid=1_ref=ogpTW5fmXR" 或 "id=1.1_ref=xxx" */ function buildScene(opts) { const parts = [] if (opts.mid != null && opts.mid !== '') parts.push(`mid=${opts.mid}`) if (opts.id) parts.push(`id=${opts.id}`) if (opts.ref) parts.push(`ref=${opts.ref}`) return parts.join(SEP) } /** * 解码:从 options.scene 解析出 mid、id、ref(支持 & 或 _ 分隔) * @param {string} sceneStr 原始 scene(可能未 decodeURIComponent) * @returns {{ mid: number, id: string, ref: string }} */ function parseScene(sceneStr) { const res = { mid: 0, id: '', ref: '' } if (!sceneStr || typeof sceneStr !== 'string') return res const decoded = decodeURIComponent(String(sceneStr)).trim() const parts = decoded.split(/[&_]/) for (const part of parts) { const eq = part.indexOf('=') if (eq > 0) { const k = part.slice(0, eq) const v = part.slice(eq + 1) if (k === 'mid') res.mid = parseInt(v, 10) || 0 if (k === 'id' && v) res.id = v if (k === 'ref' && v) res.ref = v } } return res } module.exports = { buildScene, parseScene }