SecureSocial (Play Framework) 的 MongoUserService 多次保存同一个用户

MongoUserService for SecureSocial (Play Framework) persists the same user multiple times

这是我对 UserService class 的实现。这是此实现的略微修改版本 (http://www.shrikar.com/blog/2013/10/26/playframework-securesocial-and-mongodb/)

我将它与默认的 securesocial FacebookProvider 一起使用。一切正常,除了每次登录时它都会重复用户条目这一事实。我只想保留用户一次。我认为这是由插件本身处理的,但我不确定是不是这样。是否由我来检查用户是否存在并仅在同一用户没有其他条目时才插入已登录的用户?还是其他什么地方不对?

class MongoUserService(application: Application) extends UserServicePlugin(application) with Controller with MongoController {
  def collection: JSONCollection = db.collection[JSONCollection]("users")
  def tokens: JSONCollection = db.collection[JSONCollection]("tokens")
  val outPutUser = (__ \ "id").json.prune

  def retIdentity(json: JsObject): Identity = {
    val userid = (json \ "userid").as[String]

    val provider = (json \ "provider").as[String]
    val firstname = (json \ "firstname").as[String]
    val lastname = (json \ "lastname").as[String]
    val email = (json \ "email").as[String]
    val avatar = (json \ "avatar").as[String]
    val hash = (json \ "password" \ "hasher").as[String]
    val password = (json \ "password" \ "password").as[String]
    println("password : " + password)
    val salt = (json \ "password" \ "salt").asOpt[String]
    val authmethod = (json \ "authmethod").as[String]

    val identity: IdentityId = new IdentityId(userid, authmethod)
    val authMethod: AuthenticationMethod = new AuthenticationMethod(authmethod)
    val pwdInfo: PasswordInfo = new PasswordInfo(hash, password)
    val serial:Integer=((json\"serial").as[Long]).toInt
    val user: NWOUser = new NWOUser(identity, firstname, lastname, firstname, Some(email), Some(avatar), authMethod, None, None, Some(pwdInfo),serial)
    user
  }

  def generateSerial():Integer={
    val collection = db[JSONCollection]("serial")
    val cursor = collection.find(Json.obj()).cursor[JsObject]
    val futureserial = cursor.headOption.map {
      case Some(i) => i
      case None => 0
    }
    val jobj = Await.result(futureserial, 5 seconds)
    val newSerial=jobj match {
      case x: Boolean => 0
      case _ =>retSerial(jobj.asInstanceOf[JsObject])+1

    }
    collection.update(Json.obj(), Json.obj("$set"->Json.obj("serial"->newSerial))).onComplete {
  case _=>println("updated")
}
    newSerial
  }
  def retSerial(json: JsObject): Integer = {
    println(json)
     val serial=(json \ "serial").as[Long]
     serial.toInt
  }

  def findByEmailAndProvider(email: String, providerId: String): Option[Identity] = {
    val cursor = collection.find(Json.obj("userid" -> email, "provider" -> providerId)).cursor[JsObject]
    val futureuser = cursor.headOption.map {
      case Some(user) => user
      case None => false
    }
    val jobj = Await.result(futureuser, 5 seconds)

    jobj match {
      case x: Boolean => None
      case _ => Some(retIdentity(jobj.asInstanceOf[JsObject]))

    }
  }

  def save(user: Identity): Identity = {

    val email = user.email match {
      case Some(email) => email
      case _ => "N/A"
    }

    val avatar = user.avatarUrl match {
      case Some(url) => url
      case _ => "N/A"
    }
    val savejson = Json.obj(
      "userid" -> user.identityId.userId,
      "provider" -> user.identityId.providerId,
      "firstname" -> user.firstName,
      "lastname" -> user.lastName,
      "email" -> email,
      "avatar" -> avatar,
      "authmethod" -> user.authMethod.method,
      "serial"->this.generateSerial.toLong,
       user.passwordInfo match {
       case None =>"password" -> Json.obj(   "hasher" -> "",
          "password" -> "",
          "salt" -> "")

       case x =>"password" -> Json.obj(
            "hasher" -> x.get.hasher,
          "password" -> x.get.password,
          "salt" -> x.get.salt)

      },
      "created_at" -> Json.obj("$date" -> new Date()),
      "updated_at" -> Json.obj("$date" -> new Date()))
    println(Json.toJson(savejson))
    collection.insert(savejson)
    user
  }

  def find(id: IdentityId): Option[Identity] = {
    findByEmailAndProvider(id.userId, id.providerId)
  }

  def save(token: Token) {
    val tokentosave = Json.obj(
      "uuid" -> token.uuid,
      "email" -> token.email,
      "creation_time" -> Json.obj("$date" -> token.creationTime),
      "expiration_time" -> Json.obj("$date" -> token.expirationTime),
      "isSignUp" -> token.isSignUp)
    tokens.save(tokentosave)
  }

  def findToken(token: String): Option[Token] = {

    val cursor = tokens.find(Json.obj("uuid" -> token)).cursor[JsObject]
    val futureuser = cursor.headOption.map {
      case Some(user) => user
      case None => false
    }
    val jobj = Await.result(futureuser, 5 seconds)
    jobj match {
      case x: Boolean => None
      case obj: JsObject => {
        println(obj)
        val uuid = (obj \ "uuid").as[String]
        val email = (obj \ "email").as[String]
        val created = (obj \ "creation_time" \ "$date").as[Long]
        val expire = (obj \ "expiration_time" \ "$date").as[Long]
        val signup = (obj \ "isSignUp").as[Boolean]
        val df = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
        Some(new Token(uuid, email, new DateTime(created), new DateTime(expire), signup))
      }
    }
  }

  def deleteToken(uuid: String) {}

  def deleteExpiredTokens() {}
}  

您可以使用 db.collection.[update][1]upsert=true 而不是 insert 来解决它。有了这个标志,它会在调用中插入一个由第一个元素(查询)标识的对象,当它不存在时,并更新现有的。

所以,而不是

collection.insert(savejson)

尝试

collection.update(
    Json.obj("userid" -> user.identityId.userId), 
    savejson, 
    Json.obj("upsert" -> true)
)