-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
919 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
...n/tw/waterballsa/utopia/utopiagamification/achievement/application/presenter/Presenter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.application.presenter | ||
|
||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.events.AchievementAchievedEvent | ||
|
||
interface Presenter { | ||
fun present(event: List<AchievementAchievedEvent>) | ||
} |
9 changes: 9 additions & 0 deletions
9
...lsa/utopia/utopiagamification/achievement/application/repository/AchievementRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.application.repository | ||
|
||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Type | ||
|
||
interface AchievementRepository { | ||
|
||
fun findByType(type: Type): List<Achievement> | ||
} |
10 changes: 10 additions & 0 deletions
10
...lsa/utopia/utopiagamification/achievement/application/repository/ProgressionRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.application.repository | ||
|
||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.* | ||
|
||
interface ProgressionRepository { | ||
|
||
fun findByPlayerIdAndAchievementType(playerId: String, type: Type): Map<Name, Progression> | ||
|
||
fun save(progression: Progression) | ||
} |
89 changes: 89 additions & 0 deletions
89
...a/utopia/utopiagamification/achievement/application/usecase/ProgressAchievementUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.application.usecase | ||
|
||
import org.springframework.stereotype.Component | ||
import tw.waterballsa.utopia.utopiagamification.achievement.application.presenter.Presenter | ||
import tw.waterballsa.utopia.utopiagamification.achievement.application.repository.AchievementRepository | ||
import tw.waterballsa.utopia.utopiagamification.achievement.application.repository.ProgressionRepository | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.* | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Type.TEXT_MESSAGE | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.actions.Action | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.actions.SendMessageAction | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.events.AchievementAchievedEvent | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Player | ||
import tw.waterballsa.utopia.utopiagamification.repositories.PlayerRepository | ||
import tw.waterballsa.utopia.utopiagamification.repositories.exceptions.NotFoundException.Companion.notFound | ||
|
||
@Component | ||
class ProgressAchievementUseCase( | ||
private val progressionRepository: ProgressionRepository, | ||
private val achievementRepository: AchievementRepository, | ||
private val playerRepository: PlayerRepository | ||
) { | ||
|
||
/** | ||
* Usecase flow: | ||
* 1. (DB) find progressions by playerId and achievement Type | ||
* 2. create an action | ||
* 3. progress the action, return an event | ||
* 3-1. achievement progress action, return the result of the action (Boolean) | ||
* 3-2. if meet achievement condition, refresh the progression (count++) | ||
* 3-3. achievement achieve progression, return an event | ||
* 3-3-1. if the progression count achieve the Achievement Rule, return an event | ||
* 3-3-2. reward.reward(player) | ||
* 4. (DB) persist the progression and player | ||
* 5. present the events | ||
*/ | ||
fun execute(request: Request, presenter: Presenter) { | ||
with(request) { | ||
// 查 | ||
val player = playerRepository.findPlayerById(playerId) ?: throw notFound(Player::class).id(playerId).build() | ||
val progressions = progressionRepository.findByPlayerIdAndAchievementType(playerId, type) | ||
val achievements = achievementRepository.findByType(type) | ||
|
||
val action = request.toAction(player) | ||
|
||
// 改 | ||
val events = achievements.mapNotNull { achievement -> | ||
player.progress(action, progressions, achievement) | ||
} | ||
|
||
// 存 | ||
playerRepository.savePlayer(player) | ||
|
||
// 推 | ||
presenter.present(events) | ||
} | ||
} | ||
|
||
data class Request( | ||
val playerId: String, | ||
val type: Type, | ||
val message: String | ||
) { | ||
fun toAction(player: Player): Action { | ||
if (type == TEXT_MESSAGE) { | ||
return SendMessageAction(player, message) | ||
} else { | ||
throw IllegalArgumentException("This achievement type '$type' is undefined.") | ||
} | ||
} | ||
} | ||
|
||
private fun Player.progress( | ||
action: Action, | ||
progressions: Map<Name, Progression>, | ||
achievement: Achievement, | ||
): AchievementAchievedEvent? { | ||
|
||
val progression = progressions.findProgressionByAchievement(achievement) | ||
val refreshedProgression = achievement.progressAction(action, progression) | ||
|
||
progressionRepository.save(refreshedProgression) | ||
|
||
return achievement.achieve(this, refreshedProgression) | ||
} | ||
|
||
private fun Map<Name, Progression>.findProgressionByAchievement(achievement: Achievement): Progression? = | ||
this[achievement.name] | ||
} |
87 changes: 87 additions & 0 deletions
87
...n/tw/waterballsa/utopia/utopiagamification/achievement/domain/achievements/Achievement.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements | ||
|
||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.actions.Action | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.events.AchievementAchievedEvent | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Player | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Reward | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.RoleType | ||
import java.util.UUID.randomUUID | ||
|
||
abstract class Achievement( | ||
val name: Name, | ||
val type: Type, | ||
private val condition: Condition, | ||
private val rule: Rule, | ||
private val reward: Reward, | ||
) { | ||
|
||
fun progressAction(action: Action, progression: Progression?): Progression { | ||
val newProgression = Progression(randomUUID().toString(), action.player.id, name, type) | ||
|
||
return progress(action, progression ?: newProgression) | ||
} | ||
|
||
/** | ||
* progress flow | ||
* 1. The player do an action | ||
* 2. check this action is meet the condition of achievement, and return the progression | ||
* 2-1. if meet the condition, return the progression | ||
* 2-2. if not, return null | ||
*/ | ||
private fun progress(action: Action, progression: Progression): Progression = | ||
if (condition.meet(action)) progression.refresh() else progression | ||
|
||
/** | ||
* achieve flow | ||
* 1. Get the progression from progress flow | ||
* 2. check this progression is achieved the achievement | ||
* and player has not achieved this achievement | ||
* 2-1. if achieved, reward the player and return the achievement-progressed-event | ||
* 2-2. if not, return null | ||
*/ | ||
fun achieve(player: Player, progression: Progression): AchievementAchievedEvent? { | ||
if (rule.isAchieved(player, progression)) { | ||
reward.reward(player) | ||
return toAchieveEvent() | ||
} | ||
return null | ||
} | ||
|
||
protected fun toAchieveEvent(): AchievementAchievedEvent = AchievementAchievedEvent(reward) | ||
|
||
enum class Name { | ||
LONG_ARTICLE, | ||
TOPIC_MASTER, | ||
} | ||
|
||
enum class Type { | ||
TEXT_MESSAGE | ||
} | ||
|
||
class Progression( | ||
val id: String, | ||
val playerId: String, | ||
val name: Name, | ||
val type: Type, | ||
var count: Int = 0, | ||
) { | ||
fun refresh(): Progression { | ||
count++ | ||
return this | ||
} | ||
|
||
fun isAchieved(achievementCount: Int): Boolean = count == achievementCount | ||
} | ||
|
||
interface Condition { | ||
fun meet(action: Action): Boolean | ||
} | ||
|
||
class Rule( | ||
private val role: RoleType, | ||
private val achievedCount: Int | ||
) { | ||
fun isAchieved(player: Player, progression: Progression): Boolean = | ||
!player.hasRole(role.name) && progression.isAchieved(achievedCount) | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
...allsa/utopia/utopiagamification/achievement/domain/achievements/LongArticleAchievement.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements | ||
|
||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Name.LONG_ARTICLE | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Type.TEXT_MESSAGE | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.actions.Action | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.actions.SendMessageAction | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Reward | ||
|
||
class LongArticleAchievement( | ||
condition: Condition, | ||
rule: Rule, | ||
reward: Reward | ||
) : Achievement( | ||
LONG_ARTICLE, | ||
TEXT_MESSAGE, | ||
condition, | ||
rule, | ||
reward | ||
) { | ||
|
||
/** | ||
* LongArticle.Condition:判斷此次發送的訊息字數是否達到 800 字 | ||
*/ | ||
class Condition( | ||
private val wordLength: Int | ||
) : Achievement.Condition { | ||
override fun meet(action: Action): Boolean = | ||
action is SendMessageAction && action.contentWordRequirement(wordLength) | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
...allsa/utopia/utopiagamification/achievement/domain/achievements/TopicMasterAchievement.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements | ||
|
||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Name.TOPIC_MASTER | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Type.TEXT_MESSAGE | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.actions.Action | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Reward | ||
|
||
|
||
class TopicMasterAchievement( | ||
condition: Condition, | ||
rule: Rule, | ||
reward: Reward | ||
) : Achievement( | ||
TOPIC_MASTER, | ||
TEXT_MESSAGE, | ||
condition, | ||
rule, | ||
reward | ||
) { | ||
|
||
class Condition : Achievement.Condition { | ||
override fun meet(action: Action): Boolean = true | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
...main/kotlin/tw/waterballsa/utopia/utopiagamification/achievement/domain/actions/Action.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.domain.actions | ||
|
||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Progression | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Type | ||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.events.AchievementAchievedEvent | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Player | ||
|
||
open class Action( | ||
val type: Type, | ||
val player: Player | ||
) { | ||
/** | ||
* 1. progress the action, return a progression | ||
* 2. received progression achieve progress, return an AchievementEvent | ||
*/ | ||
fun progress(achievement: Achievement, progression: Progression): Progression = | ||
achievement.progressAction(this, progression) | ||
|
||
fun achieve(achievement: Achievement, progression: Progression): AchievementAchievedEvent? = | ||
achievement.achieve(player, progression) | ||
} |
12 changes: 12 additions & 0 deletions
12
.../tw/waterballsa/utopia/utopiagamification/achievement/domain/actions/SendMessageAction.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.domain.actions | ||
|
||
import tw.waterballsa.utopia.utopiagamification.achievement.domain.achievements.Achievement.Type.TEXT_MESSAGE | ||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Player | ||
|
||
open class SendMessageAction( | ||
player: Player, | ||
private val words: String, | ||
) : Action(TEXT_MESSAGE, player) { | ||
|
||
fun contentWordRequirement(wordLength: Int): Boolean = words.length >= wordLength | ||
} |
7 changes: 7 additions & 0 deletions
7
...terballsa/utopia/utopiagamification/achievement/domain/events/AchievementAchievedEvent.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package tw.waterballsa.utopia.utopiagamification.achievement.domain.events | ||
|
||
import tw.waterballsa.utopia.utopiagamification.quest.domain.Reward | ||
|
||
class AchievementAchievedEvent( | ||
val reward: Reward, | ||
) |
Oops, something went wrong.