|
@@ -45,19 +45,30 @@ class DemoInMem {
|
|
|
|
|
|
bank.exchange(bank.getSelfAccount(), emitter, 1_300_000)
|
|
|
(0 until 10).forEach { personMap.computeIfAbsent(UUID.randomUUID()) { id -> DemoHuman(id, Nomen.randomName()) } }
|
|
|
- personMap.keys.map { bank.addAccount(it) }.subList(0, 1).forEach {
|
|
|
+ personMap.keys.map { bank.addAccount(it) }.subList(0, 10).forEach {
|
|
|
bank.exchange(it, bank.getSelfAccount(), 1_200 + (500 * Math.random()).roundToLong())
|
|
|
.thenRun({ log.info("exchange ok") })
|
|
|
.exceptionally({ log.error("exchange error"); null })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private fun doRand0() {
|
|
|
+ val fromPerson = personMap.keys.random()
|
|
|
+ val toPerson = personMap.keys.subtract(setOf(fromPerson)).random()
|
|
|
+ val fromAccount = bank.getAccounts(fromPerson).first()
|
|
|
+ val toAccout = bank.getAccounts(toPerson).first()
|
|
|
+ val dir = if (fromAccount.overall >= toAccout.overall) fromAccount to toAccout else toAccout to fromAccount
|
|
|
+ val x = Math.min(Math.round(dir.first.overall * 0.9), Math.max(0, Math.round(dir.first.overall * Math.random())))
|
|
|
+ bank.exchange(dir.second, dir.first, x)
|
|
|
+ }
|
|
|
+
|
|
|
@Scheduled(initialDelay = 10 * 1000L, fixedDelay = 5 * 1000L)
|
|
|
fun tick() {
|
|
|
+ doRand0()
|
|
|
log.info("tick")
|
|
|
}
|
|
|
|
|
|
- @Scheduled(initialDelay = 60 * 1000L, fixedDelay = 60 * 1000L)
|
|
|
+ //@Scheduled(initialDelay = 60 * 1000L, fixedDelay = 60 * 1000L)
|
|
|
fun calc() {
|
|
|
emitter.calc()
|
|
|
}
|
|
@@ -143,7 +154,7 @@ class GlobalBank : Bank {
|
|
|
} else if (e.from.id == selfAccount.id) {
|
|
|
false
|
|
|
} else {
|
|
|
- TODO()
|
|
|
+ false
|
|
|
}
|
|
|
} else {
|
|
|
true
|
|
@@ -152,7 +163,10 @@ class GlobalBank : Bank {
|
|
|
e.to.accept(coinsExact)
|
|
|
if (e.to.id != selfAccount.id) emitter.free(coinsExact)
|
|
|
globalQueue.offerLast(ExchangeSuccessEvent(UUID.randomUUID(), e))
|
|
|
- } else {
|
|
|
+ } else if (e.from.id == selfAccount.id) {
|
|
|
+ if (e.retry)
|
|
|
+ throw IllegalArgumentException("already handled event retry, failed")
|
|
|
+ e.retry = true
|
|
|
e.from.accept(coinsExact)
|
|
|
if (coinsCashback.isNotEmpty()) {
|
|
|
val invCoinsCashback = coinsCashback.map { it as DemoCashbackCoin }.map { it.invert() }
|
|
@@ -163,6 +177,25 @@ class GlobalBank : Bank {
|
|
|
e.from.accept(listOf(coin))
|
|
|
}
|
|
|
}
|
|
|
+ } else {
|
|
|
+ val fromFake: BankAccount = DemoSelfAccount(UUID.randomUUID())
|
|
|
+ fromFake.accept(coinsExact)
|
|
|
+ log.info("${fromFake} hold ${fromFake.overall} from ${e.from}")
|
|
|
+ val invCoins = coinsCashback.map { it as DemoCashbackCoin }.map { it.invert() }.map {
|
|
|
+ val coinId = emitter.emitOne(it)
|
|
|
+ emitter.extractOne(coinId)
|
|
|
+ }
|
|
|
+ val backAmount = fromFake.overall - e.amount
|
|
|
+ fromFake.accept(invCoins)
|
|
|
+ val extraCoins = fromFake.extractMoreOrExact(e.amount)
|
|
|
+ val backCoinsCashback = fromFake.extractMoreOrExact(backAmount)
|
|
|
+ val backInvCoins = backCoinsCashback.filter { it.id == null }.map { it as DemoCashbackCoin }.map { it.invert() }.map {
|
|
|
+ val coinId = emitter.emitOne(it)
|
|
|
+ emitter.extractOne(coinId)
|
|
|
+ }
|
|
|
+ e.from.accept(extraCoins + backInvCoins)
|
|
|
+ selfAccount.accept(backCoinsCashback.filter { it.id != null })
|
|
|
+ ok
|
|
|
}
|
|
|
ok
|
|
|
}
|
|
@@ -259,7 +292,7 @@ class ExchangeFailedEvent(eventId: UUID, val parentEvent: ExchangeStartEvent, va
|
|
|
|
|
|
class ExchangeFailedException(val event: ExchangeBankEvent, message: String = "exchange failed") : Exception(message)
|
|
|
|
|
|
-class ExchangeStartEvent(eventId: UUID, val to: BankAccount, val from: BankAccount, val amount: Long) : ExchangeBankEvent(eventId)
|
|
|
+class ExchangeStartEvent(eventId: UUID, val to: BankAccount, val from: BankAccount, val amount: Long, var retry: Boolean = false) : ExchangeBankEvent(eventId)
|
|
|
|
|
|
@Service
|
|
|
class DemoInMemEmitter : GlobalEmitter {
|
|
@@ -505,6 +538,8 @@ class CoinUtils {
|
|
|
|
|
|
companion object {
|
|
|
|
|
|
+ val avaiues = CoinValue.values().flatMap { value -> (0..0/*(maxEraValue - 1)*/).map { era -> value to era to (value.amount - (era * value.delta)) } }.map { it.second to it.first }.toMap()
|
|
|
+
|
|
|
fun makeSomeFake(cashback: Long): Collection<Coin> {
|
|
|
val ret = mutableListOf<Coin>()
|
|
|
var totalCashback = cashback
|
|
@@ -538,10 +573,18 @@ class CoinUtils {
|
|
|
} else {
|
|
|
val smallestGt = leastCoinMap.keys.sorted().firstOrNull()
|
|
|
if (smallestGt != null) {
|
|
|
- val cashbackAmount = diffAmount - smallestGt
|
|
|
- totalAmount += smallestGt
|
|
|
- val coinId = leastCoinMap.get(smallestGt)?.first()!!
|
|
|
- usedCoins.add(coinId)
|
|
|
+ val cashbackAmount = if (smallestGt >= amount && smallestGt > totalAmount) {
|
|
|
+ usedCoins.clear() //одной монетой перекрываем всё предыдущее
|
|
|
+ totalAmount = smallestGt
|
|
|
+ val coinId = leastCoinMap.get(smallestGt)?.first()!!
|
|
|
+ usedCoins.add(coinId)
|
|
|
+ amount - smallestGt
|
|
|
+ } else {
|
|
|
+ totalAmount += smallestGt
|
|
|
+ val coinId = leastCoinMap.get(smallestGt)?.first()!!
|
|
|
+ usedCoins.add(coinId)
|
|
|
+ diffAmount - smallestGt
|
|
|
+ }
|
|
|
ret.addAll(makeSomeFake(cashbackAmount))
|
|
|
} else {
|
|
|
TODO()
|
|
@@ -567,6 +610,26 @@ class CoinUtils {
|
|
|
val neg = sum(coins.filter { it.id == null })
|
|
|
return if (neg != 0L) "${pos + neg}(${pos}${neg})" else "$pos"
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ fun makeSomeVariants(x: Long): List<Map<Pair<CoinValue, Int>, Int>> {
|
|
|
+ val baseValues = avaiues.keys
|
|
|
+ val variants: MutableSet<Set<Long>> = mutableSetOf()
|
|
|
+ var calcFn: ((excl: Set<Long>) -> Unit)? = null
|
|
|
+
|
|
|
+ calcFn = { skip ->
|
|
|
+ val checkValues = baseValues - skip
|
|
|
+ if (checkValues.isNotEmpty()) variants.add(checkValues)
|
|
|
+ checkValues.forEach { x -> calcFn!!(skip + setOf(x)) }
|
|
|
+ }
|
|
|
+
|
|
|
+ return if (x > 0L) {
|
|
|
+ calcFn(setOf())
|
|
|
+ return listOf()
|
|
|
+ } else {
|
|
|
+ return listOf()
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|