AV.User.current().associateWithMiniApp()报错A unique field was given a value that is already taken

我的项目是先有移动端和网站端,然后才开发的小程序端,因此用户账户以手机号码+密码登录为主。现在由于要做小程序端的支付,因此需要小程序登录。

我直接调用AV.User.current().associateWithMiniApp()方法来将当前已登录的用户账户(手机号+密码)与当前微信客户端的微信账号绑定,以避免出现调用AV.User.loginWithMiniApp()方法导致当前账户切换成微信登录的账户的问题。如果我没理解错的话,我的业务需求似乎就应该这样来实现。但是问题来了,调用associateWithMiniApp()报错:

associateWithMiniApp() error: A unique field was given a value that is already taken. [400 PUT https://api.martaik.com/1.1/classes/_User/60635adcef9b462612fcdd1b](env: Windows,mp,1.05.2108130; lib: 2.17.3)

我检查了_User表中我当前登录的这个用户的数据,authData列的值是undefined,即使我使用“删除”按钮后再调用associateWithMiniApp()方法,依然报同样的错。

请问,是我定位问题错了,还是我方法用得不对呢?

这个报错是因为用重复的值。请您先检查下:

  1. 有没有自己手动建过 authData 的索引,并且创建索引时没有勾选「允许缺失值」?
  2. 是否 _User 表中的其他用户创建或绑定过同一微信账号?

如果以上 2 条均不符合的话,请提供下应用的 App ID 和可以复现问题的测试用户的 objectId,以便调查。

我查了一下,确实存在使用该微信号登录而创建的用户账号,即符合您说的第2种情况。但这种情况不止一个,有些用户本身就是用微信直接创建的账号的,那么该如何将同一用户用手机号注册的账号与用微信号登录创建的账号整合起来呢?谢谢!

你看文档这一节 绑定现有用户 能否解决你的问题.

如果你的应用已经在使用 LeanCloud 的用户系统,或者用户已经通过其他方式注册了你的应用(比如在 Web 端通过用户名密码注册),可以通过在小程序中调用 AV.User#associateWithMiniApp() 来关联已有的账户。

// 首先,使用用户名与密码登录一个已经存在的用户
AV.User.logIn('username', 'password').then(user => {
  // 将当前的微信用户与当前登录用户关联
  return user.associateWithMiniApp();
}).catch(console.error);

@linlin 我用的就是这个方法,但是报错了,报的错就是我这篇帖子一开始说得问题。

似乎在调用user.associateWithMiniApp()之前,当前微信账号不能调用AV.User.loginWithMiniApp()方法,否则就会出现A unique field was given a value that is already taken错误。

还是需要 App ID 和出现这个问题的用户 objectId,以及这部分登录需求相关的代码,来进一步排查。

避免出现调用AV.User.loginWithMiniApp()方法导致当前账户切换成微信登录的账户的问题

这里的逻辑似乎有一些问题。按照文档中,在小程序里绑定已有用户的思路,我感觉是:

1.用户用微信账户登录小程序:

AV.User.loginWithMiniApp().then(user => {
  this.globalData.user = user;
}).catch(console.error);

如果用户是第一次使用微信账号登录此应用,调用登录 API 会创建一个新的用户,可以在 控制台 > 存储 中的 _User 表中看到该用户的信息,如果用户曾经使用该方式登录过此应用(存在对应 openid 的用户),再次调用登录 API 会返回同一个用户。

2.然后在小程序中,使用 用户名与密码 登一个已经存在 _User 表的账户,与当前的微信用户关联:

AV.User.logIn('username', 'password').then(user => {
  return user.associateWithMiniApp();
}).catch(console.error);

@linlin 您好!问题就在于,如果一个用户在_User表中既有手机号+密码的账户,又有用微信登录而生成的账户。那么这个时候,要想用

AV.User.logIn('username', 'password').then(user => {
  return user.associateWithMiniApp();
}).catch(console.error);

就报错了,报的错就是A unique field was given a value that is already taken,而我怀疑就是因为用户之前用微信账号直接登录过,造成微信的authData往_User表里的手机号+密码那条用户数据里写的时候,发现要写入的authData数据已经在用微信直接登录的那个账号里已经存在了。

这就像一个用户用邮箱注册了一个账号 A ,之后又用手机注册了一个账号 B,后来又想在账号 A 上绑定自己的手机号,发现绑不上了(因为手机号重复)。从产品的角度来说,可以在一开始就限制用户只能用邮箱或手机中的其中一种方式注册新账号,或者限制用户在注册时就同时提供邮箱和手机,以避免这种问题。

现在这种情况,您需要自行实现合并账号的逻辑,将相应的代码部署在云引擎或自己的服务器上。然后在客户端捕获「A unique field was given a value that is already taken」这个报错,调用您自行实现的合并账号的接口,帮用户合并账号。

@weakish,不好意思,我还是没明白。我这里没有用邮箱注册的用户,是用户先用手机号加密码注册AV.User.signUpOrlogInWithMobilePhone(),然后用户在小程序端以手机号+验证码AV.User.logInWithMobilePhoneSmsCode()的方式登录,真机调试,微信账号的手机号与用户登录的手机号是同一号码。但在调用AV.User.current().associateWithMiniApp()时还是报错:

qa_new.js:295 associateWithMiniApp() error: A unique field was given a value that is already taken. class:_User, index:authData.lc_weapp.openid_1, duplicate value:{ : "ojPJE5VRdN-WBG_hf5G2KCKL8g9o" } [400 PUT https://api.******.com/1.1/classes/_User/5fab846c7df50b3c5b331079]

******是我隐去的敏感数据,非报错信息本身内容。

鉴于始终解决不了这个问题,我把代码和App ID以及涉及到的用户objectId贴在这里吧,以便技术人员帮忙分析处理:

adapters.getAuthInfo({
    preferUnionId: true,
}).then(authInfoResult => {
    console.log('authData: ' + JSON.stringify(AV.User.current().authData)); // TODO: Remove Before Flight!
    return AV.User.current().associateWithMiniApp().then(associationResult => {
        console.info('associationResult: ' + JSON.stringify(associationResult)); // TODO: Remove Before Flight!
    }, error => {
        console.error('associateWithMiniApp() error: ' + error.message);
    });
});

App ID: ******
User ObjectID: ******

之前回复是类比同时允许邮箱、手机两种注册方式的情况,在邮箱注册的账号上绑定手机可能引起冲突的情况。您的问题是同时允许手机注册和微信注册,然后在手机注册的账号上绑定微信账号引起冲突,原理是一样的。预防办法是只允许手机注册,或者只允许微信注册,这样可以避免出现这种冲突。在已经有很多分别使用手机和微信注册的账号的情况下,解决办法则是之前回复中提到的自行实现合并账号的逻辑。