From 14aaf4f670c0d63beb31d3ed7bb3e99a13a2b074 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Tue, 3 Aug 2021 17:31:25 +0900 Subject: [PATCH 01/12] [fix] fix #1137 --- .../JdbcBackedPresentPersistence.scala | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index f6a6ad585b..1878a26c67 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -22,8 +22,8 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It sql"""INSERT INTO present (itemstack) VALUES ($stackAsBlob)""" .updateAndReturnGeneratedKey .apply() - } } + } /** * 指定したPresentIDに対応するプレゼントを物理消去する。 @@ -60,15 +60,27 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } } - override def revoke(presentID: PresentID, players: Set[UUID]): F[Unit] = Sync[F].delay { - val scopeAsSQL = players.map(_.toString) - - DB.localTx { implicit session => - // https://discord.com/channels/237758724121427969/565935041574731807/824107651985834004 - sql"""DELETE FROM present_state WHERE present_id = $presentID AND uuid IN ($scopeAsSQL)""" - .execute() + override def revoke(presentID: PresentID, players: Set[UUID]): F[Unit] = { + val existence = DB.readOnly { implicit session => + sql"""SELECT present_state FROM present_state WHERE present_id = $presentID""" + .map(_ => ()) // avoid NoExtractor + .first() .apply() } + + import cats.Applicative + existence.fold(Applicative[F].pure(())) { _ => + Sync[F].delay { + val scopeAsSQL = players.map(_.toString) + + DB.localTx { implicit session => + // https://discord.com/channels/237758724121427969/565935041574731807/824107651985834004 + sql"""DELETE FROM present_state WHERE present_id = $presentID AND uuid IN ($scopeAsSQL)""" + .execute() + .apply() + } + } + } } override def markAsClaimed(presentId: PresentID, player: UUID): F[Unit] = Sync[F].delay { From be853edb4f53c9da141158714e68a5bf6f2b2e17 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Tue, 3 Aug 2021 17:44:55 +0900 Subject: [PATCH 02/12] [fix] fix #1132 --- .../present/bukkit/command/PresentCommand.scala | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala index adb658a801..bcaa2abb82 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala @@ -70,7 +70,7 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { _ <- NonServerThreadContextShift[F].shift state <- persistence.fetchState(context.sender.getUniqueId) } yield { - val mes = state + val presents = state .toList // 配布対象外のプレゼントを除外 .filter { case (_, state) => state != PresentClaimingState.Unavailable } @@ -78,7 +78,17 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { s"ID=$id: ${decoratePresentState(state)}" } .filter(_.nonEmpty) - MessageEffect(mes) + + val lines = if (presents.isEmpty) { + List("対象のプレゼントが存在しません") + } else { + List( + "対象のプレゼント一覧:", + "------------------" + ) ::: presents + } + + MessageEffect(lines) } eff.toIO From a0c1d39b76f075ef5e9cd61cbabce3556f02c04b Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Tue, 3 Aug 2021 17:46:19 +0900 Subject: [PATCH 03/12] [fix] fix #1136 --- .../present/infrastructure/JdbcBackedPresentPersistence.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 1878a26c67..de3c60a7cd 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -117,6 +117,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It |SELECT present_id, claimed |FROM present_state |WHERE uuid = ${player.toString} AND present_id IN ($idSliceWithPagination) + |ORDER BY present_id """ .stripMargin .map(wrapResultForState) From 38a348bca7d97afed0926870345d5feae7134654 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Tue, 3 Aug 2021 19:31:29 +0900 Subject: [PATCH 04/12] [fix] fix #1133 --- .../bukkit/command/PresentCommand.scala | 13 ++++++------ .../domain/PaginationRejectReason.scala | 10 +++++++++ .../JdbcBackedPresentPersistence.scala | 21 ++++++++++++------- 3 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PaginationRejectReason.scala diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala index bcaa2abb82..389722b98d 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala @@ -12,7 +12,7 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder import com.github.unchama.seichiassist.domain.actions.UuidToLastSeenName import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult -import com.github.unchama.seichiassist.subsystems.present.domain.{PresentClaimingState, PresentPersistence} +import com.github.unchama.seichiassist.subsystems.present.domain.{PaginationRejectReason, PresentClaimingState, PresentPersistence} import com.github.unchama.seichiassist.util.Util import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} @@ -128,11 +128,12 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { val eff = for { _ <- NonServerThreadContextShift[F].shift states <- persistence.fetchStateWithPagination(player, perPage, page) - messageLine = states - .map { case (id, state) => - s"ID=$id: ${decoratePresentState(state)}" - } - .toList + messageLine = states.fold({ + case PaginationRejectReason.TooLargePage(max) => + List(s"ページ数が大きすぎます。${max}ページ以下にしてください") + }, b => b.map { case (id, state) => + s"ID=$id: ${decoratePresentState(state)}" + }.toList) } yield { MessageEffect(messageLine) } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PaginationRejectReason.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PaginationRejectReason.scala new file mode 100644 index 0000000000..8aa1df34e7 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PaginationRejectReason.scala @@ -0,0 +1,10 @@ +package com.github.unchama.seichiassist.subsystems.present.domain + +import eu.timepit.refined.api.Refined +import eu.timepit.refined.numeric.Positive + +sealed trait PaginationRejectReason + +object PaginationRejectReason { + case class TooLargePage(exceptedMax: Int Refined Positive) extends PaginationRejectReason +} \ No newline at end of file diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index de3c60a7cd..05cda78aa0 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -111,13 +111,20 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It for { idSliceWithPagination <- idSliceWithPagination(perPage, page) } yield { - // ページネーションはIDを列挙するときにすでに完了している - val associatedEntries = DB.readOnly { implicit session => - sql""" - |SELECT present_id, claimed - |FROM present_state - |WHERE uuid = ${player.toString} AND present_id IN ($idSliceWithPagination) - |ORDER BY present_id + if (idSliceWithPagination.isEmpty) { + for { + entries <- fetchState(player) + } yield { + Left(PaginationRejectReason.TooLargePage(Math.ceil(entries.size.toDouble / perPage).toInt)) + } + } else { + // ページネーションはIDを列挙するときにすでに完了している + val associatedEntries = DB.readOnly { implicit session => + sql""" + |SELECT present_id, claimed + |FROM present_state + |WHERE uuid = ${player.toString} AND present_id IN ($idSliceWithPagination) + |ORDER BY present_id """ .stripMargin .map(wrapResultForState) From f5207ce289f83557a0c204f9553215182574ddb5 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Tue, 3 Aug 2021 19:33:37 +0900 Subject: [PATCH 05/12] =?UTF-8?q?[update]=20=E3=83=89=E3=82=AD=E3=83=A5?= =?UTF-8?q?=E3=83=A1=E3=83=B3=E3=83=88=E5=8F=8A=E3=81=B3=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../present/domain/PresentPersistence.scala | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala index fb07d50e7b..3e3d7c646c 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala @@ -8,8 +8,8 @@ import java.util.UUID /** * プレゼントシステムに関する永続化のインターフェースを規定するトレイト。 - * 他で指定がない限り、以下の条件・制約を満たす。違反した時の動作は未定義である: - * - 引数で渡される`PresentID`は対応するプレゼントが存在し、一意でなければならない + * 他で指定がない限り、以下の条件および制約を満たす。違反した時の動作は未定義である: + * - 引数で渡される`PresentID`は対応するプレゼントが存在し、その多重度は * - 返り値としての`PresentID`は対応するプレゼントが存在する */ trait PresentPersistence[F[_], ItemStack] { @@ -31,8 +31,9 @@ trait PresentPersistence[F[_], ItemStack] { def delete(presentID: PresentID): F[DeleteResult] /** - * 指定したUUIDを持つプレイヤーに対して`presentID`で指定されたプレゼントを受け取ることができるようにする。 - * + * 指定したUUIDを持つプレイヤー群に対して`presentID`で指定されたプレゼントを受け取ることができるようにする。 + * このメソッドは同じプレイヤーとプレゼントIDで呼び出された場合はべき等である。また、すでに受取可能なプレイヤーが + * `players`の中に入っていた場合は、そのプレイヤーについての受取可能にする処理をスキップする。 * @param presentID 対象のプレゼントID * @param players 受け取ることができるようにするプレイヤーのUUID * @return 永続化層への書き込みを行う作用 @@ -40,7 +41,7 @@ trait PresentPersistence[F[_], ItemStack] { def grant(presentID: PresentID, players: Set[UUID]): F[Unit] /** - * 指定したUUIDを持つプレイヤーが`presentID`で指定されたプレゼントを受け取ることができないようにする。 + * 指定したUUIDを持つプレイヤー群が`presentID`で指定されたプレゼントを受け取ることができないようにする。 * * @param presentID 対象のプレゼントID * @param players 受け取ることができないようにするプレイヤーのUUID @@ -66,7 +67,8 @@ trait PresentPersistence[F[_], ItemStack] { /** * ページネーション付きでプレイヤーがプレゼントを受け取ることができるかどうか列挙する。 - * このときの出現順序は、[[PresentID]]が最も若いエントリから先に出現する。 + * このときのページネーションは、[[PresentID]]が最も若いエントリから先に出現するように行われるが、 + * ページネーションされたMap内での各エントリの出現順序は未規定である。 * * 例として以下のような状況を仮定する: * - 既知のPresentIDとItemStackのエントリ: `List((1, aaa), (3, ccc), (6, fff), (4, ddd), (5, eee), (2, bbb))` @@ -80,18 +82,24 @@ trait PresentPersistence[F[_], ItemStack] { * `Map(1 -> Claimed, 2 -> Claimed, 3 -> Claimed, 4 -> NotClaimed, 5 -> Unavailable)` * * 備考: - * - 実装によっては、[[fetchState]]などを呼び出して既知のエントリを全列挙する可能性がある。 + * - 実装によっては、[[fetchState]]などを呼び出して有効なエントリを全列挙する可能性がある。 * - このメソッドは一貫性のために[[fetchState]]のドキュメントにある制約を継承する。 + * - 最終インデックスが有効なプレゼントの総数を超えるとき、作用はLeftを返さなければならない。 + * - 最終インデックスが有効なプレゼントの総数を超えないとき、作用はRightを返さなければならない。 * * @param player 調べる対象のプレイヤー * @param perPage ページごとのエントリの数 * @param page ページ、1オリジン * @return ページネーションを計算して返す作用 */ - def fetchStateWithPagination(player: UUID, perPage: Int Refined Positive, page: Int Refined Positive): F[Map[PresentID, PresentClaimingState]] + def fetchStateWithPagination( + player: UUID, + perPage: Int Refined Positive, + page: Int Refined Positive + ): F[Either[PaginationRejectReason, Map[PresentID, PresentClaimingState]]] /** - * プレイヤーがプレゼントを受け取ることができるかどうか列挙する。このとき、計算されるMapは次の性質を持つ: + * プレイヤーがプレゼントを受け取ることができるかどうか列挙する。このとき、計算されるMapは次の性質を満たす: * * - すでに受け取ったプレゼントに対応するPresentIDに対して[[PresentClaimingState.Claimed]]がマッピングされる * - 受け取ることができるが、まだ受け取っていないプレゼントに対応するPresentIDに対して[[PresentClaimingState.NotClaimed]]がマッピングされる From 37e71f6943680313944410c27d7ed571d2fdd666 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Tue, 3 Aug 2021 19:34:02 +0900 Subject: [PATCH 06/12] [fix] fix #1134 --- .../JdbcBackedPresentPersistence.scala | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 05cda78aa0..47069e1f99 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -2,7 +2,7 @@ package com.github.unchama.seichiassist.subsystems.present.infrastructure import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult -import com.github.unchama.seichiassist.subsystems.present.domain.{PresentClaimingState, PresentPersistence} +import com.github.unchama.seichiassist.subsystems.present.domain.{PaginationRejectReason, PresentClaimingState, PresentPersistence} import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.numeric.Positive @@ -46,17 +46,32 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } } - override def grant(presentID: PresentID, players: Set[UUID]): F[Unit] = Sync[F].delay { - import scala.collection.Seq.iterableFactory - - val initialValues = players - .map { uuid => Seq(presentID, uuid.toString, false) } - .toSeq - - DB.localTx { implicit session => - sql"""INSERT INTO present_state VALUES (?, ?, ?)""" - .batch(initialValues: _*) - .apply() + override def grant(presentID: PresentID, players: Set[UUID]): F[Unit] = { + import cats.implicits._ + for { + alreadyAddedPlayers <- Sync[F].delay { + DB.readOnly { implicit session => + sql"""SELECT uuid FROM present_state WHERE present_id = $presentID""" + .map(x => UUID.fromString(x.string("uuid"))) + .list() + .apply() + } + } + } yield { + import scala.collection.Seq.iterableFactory + + val initialValues = players + // すでに存在しているプレゼントIDとプレイヤーの組をINSERT + // すると整合性違反になるためフィルタ + .filterNot { alreadyAddedPlayers contains _ } + .map { uuid => Seq(presentID, uuid.toString, false) } + .toSeq + + DB.localTx { implicit session => + sql"""INSERT INTO present_state VALUES (?, ?, ?)""" + .batch(initialValues: _*) + .apply() + } } } @@ -106,7 +121,11 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } } - override def fetchStateWithPagination(player: UUID, perPage: Int Refined Positive, page: Int Refined Positive): F[Map[PresentID, PresentClaimingState]] = { + override def fetchStateWithPagination( + player: UUID, + perPage: Int Refined Positive, + page: Int Refined Positive + ): F[Either[PaginationRejectReason, Map[PresentID, PresentClaimingState]]] = { import cats.implicits._ for { idSliceWithPagination <- idSliceWithPagination(perPage, page) @@ -126,14 +145,15 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It |WHERE uuid = ${player.toString} AND present_id IN ($idSliceWithPagination) |ORDER BY present_id """ - .stripMargin - .map(wrapResultForState) - .toList() - .apply() - .toMap - } + .stripMargin + .map(wrapResultForState) + .toList() + .apply() + .toMap + } - filledEntries(associatedEntries, idSliceWithPagination) + Right(filledEntries(associatedEntries, idSliceWithPagination)) + } } } From 915f338d8f0b87ed540284621a13d55e26e6ddbc Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Tue, 3 Aug 2021 20:41:46 +0900 Subject: [PATCH 07/12] [fix] fix #1135 --- .../bukkit/command/PresentCommand.scala | 13 ++-- .../present/domain/GrantRejectReason.scala | 7 ++ .../domain/PaginationRejectReason.scala | 7 +- .../present/domain/PresentPersistence.scala | 2 +- .../JdbcBackedPresentPersistence.scala | 69 ++++++++++++------- 5 files changed, 63 insertions(+), 35 deletions(-) create mode 100644 src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/GrantRejectReason.scala diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala index 389722b98d..22b96ce74a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala @@ -12,7 +12,7 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder import com.github.unchama.seichiassist.domain.actions.UuidToLastSeenName import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult -import com.github.unchama.seichiassist.subsystems.present.domain.{PaginationRejectReason, PresentClaimingState, PresentPersistence} +import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence} import com.github.unchama.seichiassist.util.Util import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} @@ -323,12 +323,15 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { globalUUID2Name.keys else globalUUID2Name.filter { case (_, name) => restArg.contains(name) }.keys - errorIfNobody = if (target.isEmpty) Some(MessageEffect("対象のプレイヤーが存在しません!")) else None - _ <- persistence.grant(presentId, target.toSet) + errorIfNobody = Option.when(target.isEmpty) { MessageEffect("対象のプレイヤーが存在しません!") } + grantError <- persistence.grant(presentId, target.toSet) } yield - errorIfNobody.getOrElse(MessageEffect( + errorIfNobody.getOrElse(grantError.map { + case GrantRejectReason.NoSuchPresentID => + MessageEffect("指定されたプレゼントIDは存在しません!") + }.getOrElse(MessageEffect( s"プレゼント(id: $presentId)を受け取れるプレイヤーを追加しました。" - )) + ))) eff.toIO } else { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/GrantRejectReason.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/GrantRejectReason.scala new file mode 100644 index 0000000000..97a6dc14f1 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/GrantRejectReason.scala @@ -0,0 +1,7 @@ +package com.github.unchama.seichiassist.subsystems.present.domain + +sealed trait GrantRejectReason + +object GrantRejectReason { + case object NoSuchPresentID extends GrantRejectReason +} \ No newline at end of file diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PaginationRejectReason.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PaginationRejectReason.scala index 8aa1df34e7..110faf28d6 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PaginationRejectReason.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PaginationRejectReason.scala @@ -1,10 +1,7 @@ package com.github.unchama.seichiassist.subsystems.present.domain -import eu.timepit.refined.api.Refined -import eu.timepit.refined.numeric.Positive - sealed trait PaginationRejectReason object PaginationRejectReason { - case class TooLargePage(exceptedMax: Int Refined Positive) extends PaginationRejectReason -} \ No newline at end of file + case class TooLargePage(exceptedMax: Long) extends PaginationRejectReason +} diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala index 3e3d7c646c..6314f95360 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala @@ -38,7 +38,7 @@ trait PresentPersistence[F[_], ItemStack] { * @param players 受け取ることができるようにするプレイヤーのUUID * @return 永続化層への書き込みを行う作用 */ - def grant(presentID: PresentID, players: Set[UUID]): F[Unit] + def grant(presentID: PresentID, players: Set[UUID]): F[Option[GrantRejectReason]] /** * 指定したUUIDを持つプレイヤー群が`presentID`で指定されたプレゼントを受け取ることができないようにする。 diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 47069e1f99..15f422bc21 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -1,8 +1,9 @@ package com.github.unchama.seichiassist.subsystems.present.infrastructure +import cats.Applicative import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult -import com.github.unchama.seichiassist.subsystems.present.domain.{PaginationRejectReason, PresentClaimingState, PresentPersistence} +import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence} import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.numeric.Positive @@ -46,33 +47,47 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } } - override def grant(presentID: PresentID, players: Set[UUID]): F[Unit] = { + override def grant(presentID: PresentID, players: Set[UUID]): F[Option[GrantRejectReason]] = { import cats.implicits._ for { - alreadyAddedPlayers <- Sync[F].delay { + exists <- Sync[F].delay { DB.readOnly { implicit session => + sql"""SELECT present_id FROM present""" + .map(x => x.long("present_id")) + .list() + .apply() + }.contains(presentID) + } + } yield { + if (exists) { + val alreadyAddedPlayers = DB.readOnly { implicit session => sql"""SELECT uuid FROM present_state WHERE present_id = $presentID""" .map(x => UUID.fromString(x.string("uuid"))) .list() .apply() } - } - } yield { - import scala.collection.Seq.iterableFactory - - val initialValues = players - // すでに存在しているプレゼントIDとプレイヤーの組をINSERT - // すると整合性違反になるためフィルタ - .filterNot { alreadyAddedPlayers contains _ } - .map { uuid => Seq(presentID, uuid.toString, false) } - .toSeq - - DB.localTx { implicit session => - sql"""INSERT INTO present_state VALUES (?, ?, ?)""" - .batch(initialValues: _*) - .apply() + + import scala.collection.Seq.iterableFactory + + val initialValues = players + // すでに存在しているプレゼントIDとプレイヤーの組をINSERT + // すると整合性違反になるためフィルタ + .filterNot { alreadyAddedPlayers contains _ } + .map { uuid => Seq(presentID, uuid.toString, false) } + .toSeq + + DB.localTx { implicit session => + sql"""INSERT INTO present_state VALUES (?, ?, ?)""" + .batch(initialValues: _*) + .apply() + } + + None + } else { + Some(GrantRejectReason.NoSuchPresentID) } } + } override def revoke(presentID: PresentID, players: Set[UUID]): F[Unit] = { @@ -83,7 +98,6 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .apply() } - import cats.Applicative existence.fold(Applicative[F].pure(())) { _ => Sync[F].delay { val scopeAsSQL = players.map(_.toString) @@ -129,13 +143,10 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It import cats.implicits._ for { idSliceWithPagination <- idSliceWithPagination(perPage, page) + count <- computeValidPresentCount } yield { if (idSliceWithPagination.isEmpty) { - for { - entries <- fetchState(player) - } yield { - Left(PaginationRejectReason.TooLargePage(Math.ceil(entries.size.toDouble / perPage).toInt)) - } + Left(PaginationRejectReason.TooLargePage(Math.ceil(count.toDouble / perPage).toLong)) } else { // ページネーションはIDを列挙するときにすでに完了している val associatedEntries = DB.readOnly { implicit session => @@ -213,4 +224,14 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It val globalEntries = validGlobalId.map(id => (id, PresentClaimingState.Unavailable)).toMap globalEntries ++ knownState } + + private def computeValidPresentCount: F[Long] = Sync[F].delay { + DB.readOnly { implicit session => + sql"""SELECT COUNT(*) AS c FROM present""" + .map(rs => rs.long("c")) + .first() + .apply() + .get // safe + } + } } From fdfb0e732393f50bea8ca43223e0f24ca781743b Mon Sep 17 00:00:00 2001 From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com> Date: Tue, 3 Aug 2021 20:44:32 +0900 Subject: [PATCH 08/12] Update src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala --- .../subsystems/present/domain/PresentPersistence.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala index 6314f95360..5a7a9f1576 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala @@ -9,7 +9,7 @@ import java.util.UUID /** * プレゼントシステムに関する永続化のインターフェースを規定するトレイト。 * 他で指定がない限り、以下の条件および制約を満たす。違反した時の動作は未定義である: - * - 引数で渡される`PresentID`は対応するプレゼントが存在し、その多重度は + * - 引数で渡される`PresentID`は対応するプレゼントが存在し、その多重度は1対1である * - 返り値としての`PresentID`は対応するプレゼントが存在する */ trait PresentPersistence[F[_], ItemStack] { From e22050539b893738e9cb14f8e8ac80a1fd2504b6 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Tue, 3 Aug 2021 21:20:28 +0900 Subject: [PATCH 09/12] [fix] DeleteResult.NotFound --- .../subsystems/present/bukkit/command/PresentCommand.scala | 2 +- .../subsystems/present/domain/OperationResult.scala | 2 +- .../present/infrastructure/JdbcBackedPresentPersistence.scala | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala index 22b96ce74a..e669166752 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala @@ -269,7 +269,7 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { } yield result match { case DeleteResult.Done => MessageEffect(s"IDが${presentId}のプレゼントの消去は正常に行われました。") - case DeleteResult.NotFount => + case DeleteResult.NotFound => MessageEffect(s"IDが${presentId}のプレゼントは存在しませんでした。") } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/OperationResult.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/OperationResult.scala index ddb926eeea..ea10305e35 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/OperationResult.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/OperationResult.scala @@ -4,6 +4,6 @@ object OperationResult { sealed trait DeleteResult object DeleteResult { case object Done extends DeleteResult - case object NotFount extends DeleteResult + case object NotFound extends DeleteResult } } diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 15f422bc21..1c7971bc28 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -43,7 +43,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .update() .apply() - if (deletedRows == 1) DeleteResult.Done else DeleteResult.NotFount + if (deletedRows == 1) DeleteResult.Done else DeleteResult.NotFound } } From de43ee72984b0f68e007ea1be6d19d1bb62a6fda Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Thu, 5 Aug 2021 19:14:38 +0900 Subject: [PATCH 10/12] [update] apply review --- .../bukkit/command/PresentCommand.scala | 20 +++-- .../present/domain/PresentPersistence.scala | 12 +-- .../present/domain/RevokeWarning.scala | 8 ++ .../JdbcBackedPresentPersistence.scala | 84 ++++++++++--------- 4 files changed, 70 insertions(+), 54 deletions(-) create mode 100644 src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/RevokeWarning.scala diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala index e669166752..ecd834367f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/bukkit/command/PresentCommand.scala @@ -12,7 +12,7 @@ import com.github.unchama.minecraft.actions.OnMinecraftServerThread import com.github.unchama.seichiassist.commands.contextual.builder.BuilderTemplates.playerCommandBuilder import com.github.unchama.seichiassist.domain.actions.UuidToLastSeenName import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult -import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence} +import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence, RevokeWarning} import com.github.unchama.seichiassist.util.Util import com.github.unchama.targetedeffect.commandsender.MessageEffect import com.github.unchama.targetedeffect.{SequentialEffect, TargetedEffect} @@ -83,8 +83,7 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { List("対象のプレゼントが存在しません") } else { List( - "対象のプレゼント一覧:", - "------------------" + s"${ChatColor.GRAY}${ChatColor.UNDERLINE}対象のプレゼント一覧:${ChatColor.RESET}", ) ::: presents } @@ -383,11 +382,18 @@ class PresentCommand(implicit val ioOnMainThread: OnMinecraftServerThread[IO]) { else globalUUID2Name.filter { case (_, name) => restArg.contains(name) }.keys errorIfNobody = if (target.isEmpty) Some(MessageEffect("対象のプレイヤーが存在しません!")) else None - _ <- persistence.revoke(presentId, target.toSet) + warning <- persistence.revoke(presentId, target.toSet) } yield { - errorIfNobody.getOrElse(MessageEffect( - s"プレゼント(id: $presentId)を受け取れるプレイヤーを削除しました。" - )) + errorIfNobody.getOrElse { + warning.map { + case RevokeWarning.NoSuchPresentID => MessageEffect("そのようなプレゼントIDはありません!") + case RevokeWarning.NoPlayers => MessageEffect("対象となるプレイヤーが存在しません!") + }.getOrElse { + MessageEffect( + s"プレゼント(id: $presentId)を受け取れるプレイヤーを削除しました。" + ) + } + } } eff.toIO } else { diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala index 5a7a9f1576..5d05d0f985 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/PresentPersistence.scala @@ -47,7 +47,7 @@ trait PresentPersistence[F[_], ItemStack] { * @param players 受け取ることができないようにするプレイヤーのUUID * @return 永続化層への書き込みを行う作用 */ - def revoke(presentID: PresentID, players: Set[UUID]): F[Unit] + def revoke(presentID: PresentID, players: Set[UUID]): F[Option[RevokeWarning]] /** * 永続化層でプレゼントを受け取ったことにする。 @@ -67,8 +67,8 @@ trait PresentPersistence[F[_], ItemStack] { /** * ページネーション付きでプレイヤーがプレゼントを受け取ることができるかどうか列挙する。 - * このときのページネーションは、[[PresentID]]が最も若いエントリから先に出現するように行われるが、 - * ページネーションされたMap内での各エントリの出現順序は未規定である。 + * このときのページネーションは、[[PresentID]]が最も若いエントリから先に出現するように行われる。 + * また、ページネーションされたListの中での出現順序も、[[PresentID]]が最も若いエントリから先に出現する。 * * 例として以下のような状況を仮定する: * - 既知のPresentIDとItemStackのエントリ: `List((1, aaa), (3, ccc), (6, fff), (4, ddd), (5, eee), (2, bbb))` @@ -79,7 +79,7 @@ trait PresentPersistence[F[_], ItemStack] { * * この時 `pp.mappingWithPagination(A, 1, 5)` を呼び出すと、作用の中で計算される結果は次のとおりになる: * - * `Map(1 -> Claimed, 2 -> Claimed, 3 -> Claimed, 4 -> NotClaimed, 5 -> Unavailable)` + * `List(1 -> Claimed, 2 -> Claimed, 3 -> Claimed, 4 -> NotClaimed, 5 -> Unavailable)` * * 備考: * - 実装によっては、[[fetchState]]などを呼び出して有効なエントリを全列挙する可能性がある。 @@ -96,7 +96,7 @@ trait PresentPersistence[F[_], ItemStack] { player: UUID, perPage: Int Refined Positive, page: Int Refined Positive - ): F[Either[PaginationRejectReason, Map[PresentID, PresentClaimingState]]] + ): F[Either[PaginationRejectReason, List[(PresentID, PresentClaimingState)]]] /** * プレイヤーがプレゼントを受け取ることができるかどうか列挙する。このとき、計算されるMapは次の性質を満たす: @@ -112,7 +112,7 @@ trait PresentPersistence[F[_], ItemStack] { def fetchState(player: UUID): F[Map[PresentID, PresentClaimingState]] /** - * 指定したプレゼントIDからプレゼントを引き出す。 + * 指定したプレゼントIDでプレゼントを検索する。 * * @param presentID プレゼントID * @return 存在する場合は`Some[ItemStack]`、存在しない場合は`None`を返す作用 diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/RevokeWarning.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/RevokeWarning.scala new file mode 100644 index 0000000000..a724c97ac1 --- /dev/null +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/domain/RevokeWarning.scala @@ -0,0 +1,8 @@ +package com.github.unchama.seichiassist.subsystems.present.domain + +sealed trait RevokeWarning + +object RevokeWarning { + case object NoSuchPresentID extends RevokeWarning + case object NoPlayers extends RevokeWarning +} \ No newline at end of file diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 1c7971bc28..8dcf41ab8f 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -3,7 +3,7 @@ package com.github.unchama.seichiassist.subsystems.present.infrastructure import cats.Applicative import cats.effect.Sync import com.github.unchama.seichiassist.subsystems.present.domain.OperationResult.DeleteResult -import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence} +import com.github.unchama.seichiassist.subsystems.present.domain.{GrantRejectReason, PaginationRejectReason, PresentClaimingState, PresentPersistence, RevokeWarning} import eu.timepit.refined.api.Refined import eu.timepit.refined.auto._ import eu.timepit.refined.numeric.Positive @@ -49,7 +49,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It override def grant(presentID: PresentID, players: Set[UUID]): F[Option[GrantRejectReason]] = { import cats.implicits._ - for { + val program = for { exists <- Sync[F].delay { DB.readOnly { implicit session => sql"""SELECT present_id FROM present""" @@ -60,54 +60,58 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It } } yield { if (exists) { - val alreadyAddedPlayers = DB.readOnly { implicit session => - sql"""SELECT uuid FROM present_state WHERE present_id = $presentID""" - .map(x => UUID.fromString(x.string("uuid"))) - .list() - .apply() - } - - import scala.collection.Seq.iterableFactory - - val initialValues = players - // すでに存在しているプレゼントIDとプレイヤーの組をINSERT - // すると整合性違反になるためフィルタ - .filterNot { alreadyAddedPlayers contains _ } - .map { uuid => Seq(presentID, uuid.toString, false) } - .toSeq - - DB.localTx { implicit session => - sql"""INSERT INTO present_state VALUES (?, ?, ?)""" - .batch(initialValues: _*) - .apply() + Sync[F].delay { + val alreadyAddedPlayers = DB.readOnly { implicit session => + sql"""SELECT uuid FROM present_state WHERE present_id = $presentID""" + .map(x => UUID.fromString(x.string("uuid"))) + .list() + .apply() + } + + import scala.collection.Seq.iterableFactory + + val initialValues = players + // すでに存在しているプレゼントIDとプレイヤーの組をINSERT + // すると整合性違反になるためフィルタ + .filterNot { alreadyAddedPlayers contains _ } + .map { uuid => Seq(presentID, uuid.toString, false) } + .toSeq + + DB.localTx { implicit session => + sql""" + INSERT INTO present_state VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE present_id=present_id + """ + .batch(initialValues: _*) + .apply() + } + + None } - - None } else { - Some(GrantRejectReason.NoSuchPresentID) + // 型推論 + Applicative[F].pure(Some(GrantRejectReason.NoSuchPresentID: GrantRejectReason)) } } + program.flatten } - override def revoke(presentID: PresentID, players: Set[UUID]): F[Unit] = { - val existence = DB.readOnly { implicit session => - sql"""SELECT present_state FROM present_state WHERE present_id = $presentID""" - .map(_ => ()) // avoid NoExtractor - .first() - .apply() - } - - existence.fold(Applicative[F].pure(())) { _ => + override def revoke(presentID: PresentID, players: Set[UUID]): F[Option[RevokeWarning]] = { + if (players.isEmpty) { + Applicative[F].pure(Some(RevokeWarning.NoPlayers)) + } else { Sync[F].delay { val scopeAsSQL = players.map(_.toString) - DB.localTx { implicit session => + val deleteCount = DB.localTx { implicit session => // https://discord.com/channels/237758724121427969/565935041574731807/824107651985834004 sql"""DELETE FROM present_state WHERE present_id = $presentID AND uuid IN ($scopeAsSQL)""" - .execute() + .update() .apply() } + + Option.when(deleteCount == 0) { RevokeWarning.NoSuchPresentID } } } } @@ -139,7 +143,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It player: UUID, perPage: Int Refined Positive, page: Int Refined Positive - ): F[Either[PaginationRejectReason, Map[PresentID, PresentClaimingState]]] = { + ): F[Either[PaginationRejectReason, List[(PresentID, PresentClaimingState)]]] = { import cats.implicits._ for { idSliceWithPagination <- idSliceWithPagination(perPage, page) @@ -160,7 +164,6 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .map(wrapResultForState) .toList() .apply() - .toMap } Right(filledEntries(associatedEntries, idSliceWithPagination)) @@ -179,7 +182,6 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .map(wrapResultForState) .list() .apply() - .toMap } filledEntries(associatedEntries, validPresentIDs) @@ -220,7 +222,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It ItemStackBlobProxy.blobToItemStack(rs.string("itemstack")) } - private def filledEntries(knownState: Map[PresentID, PresentClaimingState], validGlobalId: Iterable[PresentID]) = { + private def filledEntries(knownState: List[(PresentID, PresentClaimingState)], validGlobalId: Iterable[PresentID]) = { val globalEntries = validGlobalId.map(id => (id, PresentClaimingState.Unavailable)).toMap globalEntries ++ knownState } @@ -228,7 +230,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It private def computeValidPresentCount: F[Long] = Sync[F].delay { DB.readOnly { implicit session => sql"""SELECT COUNT(*) AS c FROM present""" - .map(rs => rs.long("c")) + .map(_.long("c")) .first() .apply() .get // safe From 8b6e2f0f297d625889cfabfa3eb8c41510c255c8 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Fri, 6 Aug 2021 18:00:25 +0900 Subject: [PATCH 11/12] [fix] compile error --- .../JdbcBackedPresentPersistence.scala | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index 8dcf41ab8f..d43786098a 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -86,11 +86,12 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .apply() } - None + // 型推論 + None: Option[GrantRejectReason] } } else { // 型推論 - Applicative[F].pure(Some(GrantRejectReason.NoSuchPresentID: GrantRejectReason)) + Applicative[F].pure(Some(GrantRejectReason.NoSuchPresentID: GrantRejectReason): Option[GrantRejectReason]) } } @@ -166,7 +167,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .apply() } - Right(filledEntries(associatedEntries, idSliceWithPagination)) + Right(filledEntries(associatedEntries, idSliceWithPagination).toList) } } } @@ -184,7 +185,7 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It .apply() } - filledEntries(associatedEntries, validPresentIDs) + filledEntries(associatedEntries, validPresentIDs).toMap } } @@ -222,9 +223,9 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It ItemStackBlobProxy.blobToItemStack(rs.string("itemstack")) } - private def filledEntries(knownState: List[(PresentID, PresentClaimingState)], validGlobalId: Iterable[PresentID]) = { - val globalEntries = validGlobalId.map(id => (id, PresentClaimingState.Unavailable)).toMap - globalEntries ++ knownState + private def filledEntries(knownState: List[(PresentID, PresentClaimingState)], validGlobalId: Iterable[PresentID]): Iterable[(PresentID, PresentClaimingState)] = { + val globalEntries = validGlobalId.map(id => (id, PresentClaimingState.Unavailable)) + (globalEntries ++ knownState) } private def computeValidPresentCount: F[Long] = Sync[F].delay { From 95f68c286cebf9408d77dab0136a37ed9dcf7de2 Mon Sep 17 00:00:00 2001 From: KisaragiEffective Date: Fri, 6 Aug 2021 21:19:57 +0900 Subject: [PATCH 12/12] [update] apply review (upsert) --- .../infrastructure/JdbcBackedPresentPersistence.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala index d43786098a..f6d8160f26 100644 --- a/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala +++ b/src/main/scala/com/github/unchama/seichiassist/subsystems/present/infrastructure/JdbcBackedPresentPersistence.scala @@ -71,16 +71,14 @@ class JdbcBackedPresentPersistence[F[_] : Sync] extends PresentPersistence[F, It import scala.collection.Seq.iterableFactory val initialValues = players - // すでに存在しているプレゼントIDとプレイヤーの組をINSERT - // すると整合性違反になるためフィルタ - .filterNot { alreadyAddedPlayers contains _ } .map { uuid => Seq(presentID, uuid.toString, false) } .toSeq DB.localTx { implicit session => + // upsert - これによってfilterなしで整合性違反を起こすことはなくなる sql""" INSERT INTO present_state VALUES (?, ?, ?) - ON DUPLICATE KEY UPDATE present_id=present_id + ON DUPLICATE KEY UPDATE present_id=present_id, uuid=uuid """ .batch(initialValues: _*) .apply()