ENGINEERING NOTES · 工程笔记

一套模板服务成百上千个商户:小程序 ext_json 动态路由方案

一、背景:"每商户一个分支"是场灾难

我们的多租户小程序 SaaS,给每个商户交付独立品牌、独立 AppID 的小程序。最早的做法粗暴:主仓库拉分支,改 AppID、配色、店名,再出包提审发布。第 5 家上线时,问题集中爆发:

  • 一个 bug 要在 5 个分支各修一遍,漏一个就是"这家比那家慢三天";
  • 分支越多越不敢合并,主干新功能回不去,代码开始"分家";
  • 发版从半天膨胀到两天;新商户接入还要全量出包,接一家折腾一整天。

真正下死决心的,是分支爬到 30 多个后的某个周三下午:14 点确认支付回调验签失败的 bug,修复只有 11 行,主干 20 分钟改完自测通过;之后四个多小时,是把这 11 行 cherry-pick 到 37 个分支上——平均每三家撞一次冲突,各家为改标题、改首页模块动过同一批文件,解到第 10 个就开始怀疑前面解错了。19 点半推完最后一个,对台账还是漏了 1 家:那家改过包名结构,分支偏到没法 fast-forward,只能手工重放。收工只剩疲惫——一个 bug,我们修了 38 遍。

此后没有争议:N 个商户必须共用同一份代码,差异只能收敛到"配置"层。这正是微信开放平台第三方平台机制的标准玩法:统一模板 + ext_json 注入。

二、方案:统一模板 + 授权链路 + ext_json 注入

链路五步,每步做什么:

  1. 代码进草稿箱:开发者工具正常上传,代码进平台草稿箱。草稿箱只有一格,永远被最新上传覆盖,只当中转站,别当版本管理用;
  2. 草稿箱转模板库:调"从草稿箱添加到模板库"接口固化为模板,拿到递增的 template_id 与自填 user_version;模板库可保留多版本,才是版本锚点;
  3. 商户扫码授权:平台侧生成预授权码拼出授权链接,商户管理员扫码,把代开发、上传、提审、发布等权限托管给我们的第三方平台;回调拿到 authorizer_appid 与 refresh_token,换出约 2 小时有效、按需续期的 authorizer_access_token,此后一切代办操作都凭它;
  4. commit 注入 ext_json:调代码托管接口为某商户提交代码时,带上 template_id 和一份 ext_json,商户差异全写在这里;
  5. 启动时读 ext:运行时用 wx.getExtConfigSync() 读出 ext,merchantId 回答"我是哪家店",首页样式、接口参数全由它驱动。

(配图位:授权→草稿箱→模板库→commit→发布 时序图)

一句话概括:代码是共享的,身份是注入的

三、关键细节:三个不能含糊的地方

1. ext_json 能带什么。 只放非敏感路由参数:merchantId、主题色、首页模块开关。AppSecret、支付密钥、平台 ticket 绝不进小程序端——代码包可被解包,ext 也不是加密存储,密钥只待在服务端配置中心。

2. 启动时建立商户上下文。 时序写死三段:先同步读 ext,再请求后端换会话,最后以后端结论为准;所有请求统一携带 merchantId(请求头 X-Merchant-Id)。getExtConfigSync 在非模板环境(普通 AppID 直接预览)拿不到值,必须给兜底配置,保证任何环境启动不崩。伪代码见下节。

3. 服务端兜底隔离。 ext 对客户端完全可见,merchantId 可能被篡改:代码包可解包、ext 是明文、请求可被代理重放,客户端来的一切都只是用户输入,隔离不能靠前端自觉。登录时服务端用 code2session 换 openid,校验该用户属于哪个商户,租户身份写进服务端会话;此后数据访问由 ORM 层强制拼接租户条件(Java 21 + Spring Boot,拦截器改写 SQL)——与 PostgreSQL 行级安全(RLS)同一思路,每行带租户列、数据库层强制过滤。前端带身份只是导航,后端验身份才是边界,这条边界有专门的越权用例盯着(1241 个测试方法里相当一部分在打这类场景)。

四、脱敏配置示例(结构示意)

commit 时传的 ext_json(占位符,非真实值):

{
  "extAppid": "wx_placeholder_appid",
  "ext": {
    "merchantId": "mch-000123",
    "themeColor": "#3A5FCD",
    "homeModules": ["groupBuy", "serviceList", "memberCard"]
  },
  "window": {
    "navigationBarTitleText": "占位商户名"
  }
}

小程序端启动的三段时序(JavaScript,伪代码):

// app.js onLaunch:建立商户上下文(伪代码)
const FALLBACK = { merchantId: 'demo', themeColor: '#333333' };
// ① 同步读 ext。模板环境已随 commit 固化进代码包;
//    非模板环境(普通 AppID 预览)抛错或为空,必须兜底
let ext = FALLBACK;
try {
  const raw = wx.getExtConfigSync();
  if (raw && raw.merchantId) ext = raw;
} catch (e) { /* 保持兜底 */ }
this.globalData.merchant = ext; // 主题色、模块开关从这里取
// ② wx.login 换 code 请求后端;客户端 merchantId 只是导航提示
wx.login({
  success: ({ code }) => {
    request('/session/init', { code, merchantId: ext.merchantId })
      // ③ 以后端为准:code2session 换 openid,校验属于哪家店,
      //    签发带租户身份的服务端会话;不一致以服务端为准
      .then((srv) => { this.globalData.session = srv; })
      .catch(() => { /* 降级到演示模式 */ });
  }
});

补一句:按商户换页面集还有 extPages 可用;我们用 homeModules 开关控制显隐够用。文中不出现真实 AppID、域名、密钥,生产值只存在服务端。

五、踩坑清单

前两个坑最贵,展开成"现象→定位→修法"。

坑 1:开发工具与真机的 ext 差异。 现象:工具根目录改 ext.json 立即生效;同一套代码传到体验版和真机,改的字段纹丝不动,清缓存、删小程序、重启手机全无效,一个下午没了。定位:工具直接读本地 ext.json;真机的 ext 是 commit 时固化进代码包的,改字段等于改代码。修法:把"改 ext"当一次完整发版(重新 commit+提审+发布),写进发布 checklist 第一条。

坑 2:模板版本升级没有灰度。 现象:新模板一次性 commit 给全部商户,当晚两家会员卡页白屏——新模板接口参数变了,这两家正命中旧参数路径。定位:template_id 对一家商户全量生效,commit 下去就是全部用户;能回滚,但影响已发生。修法:先挑 1–2 家有代表性的商户 commit 新 template_id,观察 24–48 小时再推全量;台账同步更新,10 分钟内查得到哪家在哪个版本。

其余四个一行带过:

#现象与修法
3getExtConfigSync 无兜底普通 AppID 预览抛错或为空,启动即崩;try/catch + 默认商户配置
4ext.json 误进仓库模拟用 ext.json 带测试 AppID 提进 git;.gitignore 排除,只放 ext.example.json
5指望 ext 动态换域名request 合法域名在后台配置,不随 ext 变;域名统一,差异走 merchantId
6忽略商户类目差异模板一把梭提审,类目资质对不上被卡;commit 前核对类目,敏感页按类目显隐

这份清单没有一条是文档明说的,全是上线流程里真金白银换来的。切到统一模板后:主干只有一条,"修 38 遍"的下午不会再有——修复一次,随下一次 commit 推给全部商户;新商户接入从"出包一天"变成"填一份 ext_json 加一次授权"。

六、三句话总结

  1. 商户差异全部收敛进一份 ext_json,代码永远只有一套,维护成本从 O(N) 降到 O(1);
  2. ext 只放非敏感路由参数,密钥永远只在服务端;商户身份启动时读入、服务端二次校验;
  3. 开发者工具与真机对 ext 的处理不同,ext 变更按发版流程管理,模板版本要有台账。

运营主体:北京位元跃迁科技有限公司。本文同步发布于本号技术专栏,可搬运至 CSDN/掘金。

本文为位元跃迁原创内容,仅代表编辑观点,不构成经营或投资建议;文中涉及的品牌与案例仅作公开信息分析示例。

LET'S TALK

把方法用进你的生意

如果你正在为门店做数字化规划,欢迎和我们聊聊。位元跃迁是微信支付合作伙伴、微信开放平台第三方平台,提供小程序定制、开发咨询与企业数字化服务。

18601279913

业务咨询 · 项目合作 · 代理商合作

了解位元跃迁的服务 →
位元跃迁业务联系微信二维码
微信咨询