返回博客

小程序对接外部会员系统:占位名 + 逐字段锁,而非全局一刀切

把小程序注册流程砍到只留手机号,是常见的漏斗优化动作。用户不想填名字,那就先不填。问题是,背后的外部会员系统(POS / CRM)往往要求 name 字段非空才能建档,而且一旦记录写入,有人就习惯性地加一个「记录已存在 → 全部字段锁定」的逻辑,结果用户连后来想补填真实姓名都做不到。

我在一个对接第三方会员积分系统的微信小程序里踩过这个坑,来完整复盘一遍。

现象

  1. 注册时仅收集手机号,小程序在后台静默向外部系统注册会员(获取 memberCode)。
  2. 外部系统 API 返回错误:name 不能为空。
  3. 第一次修复:注册时用 nickName"新用户" 作为占位符写入 name
  4. 用户进入「个人资料编辑」页,想补填真实姓名,发现姓名栏是禁用状态——因为「记录已存在」的全局锁把所有字段都锁死了。
  5. 换一个人:这个人当初直接填了真实姓名,反而生日栏是空的,但同样被全局锁锁住,无法补填。

根因

问题出在锁的粒度错了。全局记录锁(if (record.exists) disableAll())把两类完全不同的字段混为一谈:

  • 已填真实值的字段:这些应该锁,防止用户误改。
  • 只填了占位符或根本没填的字段:这些应该开放,用户需要补全。

全局锁做不到区分,而且第二个隐患是:如果占位符没有统一前缀标记,后续代码根本无法判断 "新用户" 是真实姓名还是占位垃圾。

坑:全局记录锁 + 无标记占位符双杀

if (record.exists) { nameLocked = true; birthdayLocked = true; } 这个模式看起来「保守安全」,实际上让占位符名字永久污染 CRM,同时剥夺了用户补填真实信息的能力。没有前缀标记的占位符(如 "新用户")一旦写入,代码就再也分不清它是真名还是垃圾。

正解

三段式协作模式,缺任何一段都会破坏闭环

第一段:注册时写占位名,带可识别的前缀标记

// app.js — 静默注册前构造 name
posOpts.name =
  userInfo.realName ||
  (userInfo.nickName && "PREFIX_" + userInfo.nickName) ||
  "PREFIX_" + phone.slice(-8);

前缀(这里用 "PREFIX_" 代指,实际用品牌相关的固定字符串)是整个模式的锚点:

  • 永远不改(存量记录依赖它做正则匹配)
  • 只在这一处代码路径使用
  • CRM 导出时人工也能一眼认出哪些是占位记录

所有注册入口(主注册页、一键授权、分享链接冷启动)必须走同一个 helper,否则一半记录带前缀、一半不带,Piece 2 的正则就废了。

第二段:资料编辑页按字段逐个判断锁状态

// editMyInfo.js — 页面加载后计算各字段是否可编辑
const nameLocked     = record.name && !/^PREFIX_/.test(record.name);  // 有真实值才锁
const birthdayLocked = Boolean(record.birthday);                       // 非空才锁
const genderLocked   = Boolean(record.gender);                         // 非空才锁
  • nameLocked 用正则区分占位名和真实名,占位名对用户可编辑。
  • birthdayLocked / genderLocked 只看非空,任何非空值都视为权威数据,锁定。
  • 禁止出现 recordLocked 全局变量:一个字段填了不能影响另一个空字段的可编辑性。

第三段:提交时只 diff 发送解锁字段的变更

// editMyInfo.js — 构造 POS 更新 payload
const posUpdate = {};
if (!nameLocked     && form.name     !== origin.name)     posUpdate.name     = form.name;
if (!birthdayLocked && form.birthday !== origin.birthday) posUpdate.birthYear = yearOf(form.birthday);
if (!genderLocked   && form.gender   !== origin.gender)   posUpdate.gender   = form.gender;
 
if (Object.keys(posUpdate).length) {
  posProxyUpdate({ memberCode, ...posUpdate });
}

只把「解锁且值有变化」的字段发给外部系统,不发整个 form。好处:不会因为 form 里有一个字段的旧值被意外清空,就把外部系统里的权威数据覆盖掉。

正解:前缀标记 + 逐字段锁 + diff 提交三段协作
  • 注册时统一在占位名加固定前缀,确保后续代码可区分。
  • 编辑页对每个字段独立计算 locked 状态:有真实值才锁,占位/空值保持可编辑。
  • 提交时只 diff 解锁字段的变更,绝不全量覆盖。

一句话外卖

跨系统字段锁的粒度应该是字段级别的「是否已有权威值」,而不是记录级别的「是否已存在」——前者可以为空字段保留补填窗口,后者是个无解的死锁。