member-profile-empty-email-2026-11-15.md 3.1 KB

会员资料保存失败:空邮箱唯一键冲突

2026-11-15,用户提供熊梓含初中(ID1850)的会员详情截图:修改性别、年级后保存提示“运营云函数执行失败”。本次查明原因并完成本地修复,未部署,未提交正式资料保存。

证据链

  1. 正式账号性别、年级均未保存,updatedAt保持2026-11-15 09:06:16(北京时间)。资料审计没有本次成功保存记录。
  2. 当前网关 updateMemberProfile 即使邮箱未填写,也无条件 target.set('email', data.email),实际待写值为 ''。
  3. 真实库有 _User_unique_email 和 unique_email 两个email唯一索引;已有另一个账号的email为 '',目标账号email未设置。空字符串参与唯一约束,而多个NULL允许共存。
  4. 在授权且限时的临时诊断函数中,执行实际前置鉴权、字段校验和重复手机号检查,到该目标的save边界立即阻断写入:原代码待写邮箱确为 '',性别/年级合法,SDK自身validate无错误。没有调用正式用户save。
  5. 用实际业务函数、合成账号和相同PGlite唯一索引执行真实UPDATE,旧代码两项用例触发 duplicate key value violates unique constraint "_User_unique_email",无效邮箱校验用例正常。这与截图的后台500兜底提示相符。原请求完整异常日志未取得,不能宣称捕获了那一笔请求的原始SQL错误。

因此失败不是性别或“赣州初二”本身的问题,而是可选邮箱被错误写成了空的唯一键。数据库拒绝了整次保存,界面又用通用500文案遮住了原因。

本地修复

仅修改 updateMemberProfile:有邮箱时继续保存并执行既有格式校验;空白邮箱通过完整Parse SDK的unset清除,落为未设置值,不再写空字符串。legacyUserData.Email保留既有空字符串表示,此JSON字段没有顶层email唯一约束。

没有删除或修改那个已占用空邮箱的其他账号,没有清理用户表,没有修改唯一索引、权限、余额或收费规则。当前网关兼容层使用完整SDK,支持unset。

使用同一阻断式SDK探针检查候选:待保存email操作为 {"__op":"Delete"},性别/年级校验通过,所有写入仍被阻断。它验证真实SDK序列化与前置流程,不等于完成正式HTTP保存或线上验收。

检查与交付边界

  • 新增3项回归:空邮箱不冲突且性别/年级可保存、有效邮箱填写及主动清空、无效邮箱在保存/审计前拒绝。先2项失败/1项通过,修复后3/3通过;包含4个余额字段保持一致。
  • 独立审查3/3通过;完整服务端回归及云函数定义检查结果见同名JSON。
  • 最终读取正式账号完整对象与检查前完全一致,生产管理网关源码哈希也未改变。性别、年级仍未保存,课时余额未动。
  • 所有临时诊断函数执行后已删除。文件/日志、真实账号及源码快照仅保存在 /Users/mac/.local/state/xiaoshu/qa/2026-11-15-profile-save/,不进入Git。

状态:原因已定位,本地修复已准备;尚未上线,也未代用户提交女/赣州初二资料。