Przeglądaj źródła

мелкий фикс ссылок
детали транзакций обмена

κρμγ 5 lat temu
rodzic
commit
fbf58d8e0e

+ 2 - 1
src/main/java/inn/ocsf/bee/freigeld/core/model/Bank.java

@@ -1,6 +1,7 @@
 package inn.ocsf.bee.freigeld.core.model;
 
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
 import java.util.UUID;
@@ -20,7 +21,7 @@ public interface Bank extends PrivatePerson {
     Collection<BankAccount> getAccounts(@NotNull UUID person);
 
     @NotNull
-    CompletableFuture<Ticket> exchange(@NotNull BankAccount to, @NotNull BankAccount from, @NotNull Long amount);
+    CompletableFuture<IExchangeTicket> exchange(@NotNull BankAccount to, @NotNull BankAccount from, @NotNull Long amount, @Nullable BankExchangeDetails details);
 
     @NotNull
     Stream<BankAccount> getAccounts();

+ 59 - 0
src/main/java/inn/ocsf/bee/freigeld/core/model/BankExchangeDetails.java

@@ -0,0 +1,59 @@
+package inn.ocsf.bee.freigeld.core.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class BankExchangeDetails {
+
+    private String description;
+
+    private Map<String, Object> extraFields = new HashMap<>();
+
+    public BankExchangeDetails() {
+    }
+
+    public Map<String, Object> getExtraFields() {
+        return extraFields;
+    }
+
+    public void setExtraFields(Map<String, Object> extraFields) {
+        this.extraFields = extraFields;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public static final class BankExchangeDetailsBuilder {
+        private String description;
+        private Map<String, Object> extraFields = new HashMap<>();
+
+        private BankExchangeDetailsBuilder() {
+        }
+
+        public static BankExchangeDetailsBuilder aBankExchangeDetails() {
+            return new BankExchangeDetailsBuilder();
+        }
+
+        public BankExchangeDetailsBuilder withDescription(String description) {
+            this.description = description;
+            return this;
+        }
+
+        public BankExchangeDetailsBuilder withExtraFields(Map<String, Object> extraFields) {
+            this.extraFields = extraFields;
+            return this;
+        }
+
+        public BankExchangeDetails build() {
+            BankExchangeDetails bankExchangeDetails = new BankExchangeDetails();
+            bankExchangeDetails.setDescription(description);
+            bankExchangeDetails.setExtraFields(extraFields);
+            return bankExchangeDetails;
+        }
+    }
+}

+ 10 - 0
src/main/java/inn/ocsf/bee/freigeld/core/model/IExchangeTicket.java

@@ -0,0 +1,10 @@
+package inn.ocsf.bee.freigeld.core.model;
+
+import javax.annotation.Nullable;
+
+public interface IExchangeTicket extends ITicket {
+
+    @Nullable
+    BankExchangeDetails getDetails();
+
+}

+ 9 - 0
src/main/java/inn/ocsf/bee/freigeld/core/model/ITicket.java

@@ -0,0 +1,9 @@
+package inn.ocsf.bee.freigeld.core.model;
+
+import java.util.UUID;
+
+public interface ITicket {
+
+    UUID getId();
+
+}

+ 10 - 0
src/main/java/inn/ocsf/bee/freigeld/core/model/PublicLink.java

@@ -13,6 +13,8 @@ public abstract class PublicLink {
     @Version
     private Long version;
 
+    private PublicLinkStatus status;
+
     public PublicLink(String id) {
         this.id = id;
     }
@@ -27,4 +29,12 @@ public abstract class PublicLink {
     public void setId(String id) {
         this.id = id;
     }
+
+    public PublicLinkStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(PublicLinkStatus status) {
+        this.status = status;
+    }
 }

+ 5 - 0
src/main/java/inn/ocsf/bee/freigeld/core/model/PublicLinkStatus.java

@@ -0,0 +1,5 @@
+package inn.ocsf.bee.freigeld.core.model;
+
+public enum PublicLinkStatus {
+    active, done
+}

+ 1 - 1
src/main/java/inn/ocsf/bee/freigeld/core/model/Ticket.java

@@ -5,7 +5,7 @@ import com.querydsl.core.annotations.QueryEntity;
 import java.util.UUID;
 
 @QueryEntity
-public abstract class Ticket {
+public abstract class Ticket implements ITicket {
 
     private UUID id;
 

+ 1 - 1
src/main/kotlin/inn/ocsf/bee/freigeld/core/data/CentralBankAccountLevel.kt

@@ -95,7 +95,7 @@ abstract class CentralBankAccountLevel : CentralBank {
         if (thisAccounts.isEmpty()) {
             val selfAcc = addAccount(bankId)
             emitter.emit(CoinUtils.makeSomeFuture(10_000_000))
-            exchange(selfAcc, emitter, 5_000_000)
+            exchange(selfAcc, emitter, 5_000_000, null)
         }
         getCurrentState { }
     }

+ 32 - 20
src/main/kotlin/inn/ocsf/bee/freigeld/core/data/CentralBankQueueLevel.kt

@@ -5,6 +5,8 @@ import inn.ocsf.bee.freigeld.core.calc.CoinUtils
 import inn.ocsf.bee.freigeld.core.model.*
 import inn.ocsf.bee.freigeld.core.service.TicketService
 import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
 import org.slf4j.LoggerFactory
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.stereotype.Service
@@ -32,9 +34,9 @@ class CentralBankQueueLevel : CentralBankAccountLevel() {
 
     private val log = LoggerFactory.getLogger(javaClass)
 
-    override fun exchange(to: BankAccount, from: BankAccount, amount: Long): CompletableFuture<Ticket> {
-        val e = ExchangeStartEvent(UUID.randomUUID(), to, from, amount)
-        val ret = CompletableFuture<Ticket>()
+    override fun exchange(to: BankAccount, from: BankAccount, amount: Long, details: BankExchangeDetails?): CompletableFuture<IExchangeTicket> {
+        val e = ExchangeStartEvent(UUID.randomUUID(), to, from, amount, details)
+        val ret = CompletableFuture<IExchangeTicket>()
         globalFutureMap.computeIfAbsent(e.id) { id ->
             ticketService.offerLast(bankTicketChannelName, e)
             val def = CompletableDeferred<Ticket>()
@@ -208,24 +210,27 @@ class CentralBankQueueLevel : CentralBankAccountLevel() {
 
     @PostConstruct
     fun start() {
-        emitter.listen { e ->
-            log.debug("emitter event ${e.javaClass.name}")
-            when (e) {
-                is EmitterStartRecalculationEvent -> ticketService.offerLast(bankTicketChannelName, BankPauseOnRecalcEvent(UUID.randomUUID(), e))
-                is EmitterStopRecalculationEvent -> ticketService.offerLast(bankTicketChannelName, BankResumeAfterRecalcEvent(UUID.randomUUID(), e))
-                else -> throw RuntimeException("unknown emitter event ${e.javaClass.name}")
+        GlobalScope.launch {
+            emitter.listen { e ->
+                log.debug("emitter event ${e.javaClass.name}")
+                when (e) {
+                    is EmitterStartRecalculationEvent -> ticketService.offerLast(bankTicketChannelName, BankPauseOnRecalcEvent(UUID.randomUUID(), e))
+                    is EmitterStopRecalculationEvent -> ticketService.offerLast(bankTicketChannelName, BankResumeAfterRecalcEvent(UUID.randomUUID(), e))
+                    else -> throw RuntimeException("unknown emitter event ${e.javaClass.name}")
+                }
             }
-        }
-        ticketService.listen(bankTicketChannelName) { e ->
-            val qst = getCurrentState { it.queueState ?: BankQueueState.open }
-            if (e is AbstractBankEvent && qst != BankQueueState.closed) {
-                if ((qst == BankQueueState.open && !(e is BankResumeAfterRecalcEvent)) || (qst == BankQueueState.recalc && e is BankResumeAfterRecalcEvent)) {
-                    pollLater(e)
+            ticketService.listen(bankTicketChannelName) { e ->
+                val qst = getCurrentState { it.queueState ?: BankQueueState.open }
+                log.info("poll event ${e::class.java} on state ${qst}")
+                if (e is AbstractBankEvent && qst != BankQueueState.closed) {
+                    if ((qst == BankQueueState.open && !(e is BankResumeAfterRecalcEvent)) || (qst == BankQueueState.recalc && e is BankResumeAfterRecalcEvent)) {
+                        pollLater(e)
+                    } else {
+                        false
+                    }
                 } else {
-                    false
+                    null
                 }
-            } else {
-                null
             }
         }
     }
@@ -259,17 +264,24 @@ class ExchangeFailedEvent() : ExchangeBankEvent() {
 
 class ExchangeFailedException(val event: ExchangeBankEvent, message: String? = "exchange failed") : Exception(message)
 
-class ExchangeStartEvent() : ExchangeBankEvent() {
+class ExchangeStartEvent() : ExchangeBankEvent(), IExchangeTicket {
     var to: UUID? = null
     var from: UUID? = null
     var amount: Long? = null
+    var _details: BankExchangeDetails? = null
 
-    constructor(id: UUID, to: BankAccount, from: BankAccount, amount: Long) : this() {
+    constructor(id: UUID, to: BankAccount, from: BankAccount, amount: Long, details: BankExchangeDetails?) : this() {
         this.id = id
         this.to = to.id
         this.from = from.id
         this.amount = amount
+        this._details = details
+    }
+
+    override fun getDetails(): BankExchangeDetails? {
+        return _details
     }
+
 }
 
 class BankPauseOnRecalcEvent() : AbstractBankEvent() {

+ 17 - 8
src/main/kotlin/inn/ocsf/bee/freigeld/core/data/GlobalEmitterImpl.kt

@@ -12,8 +12,10 @@ import inn.ocsf.bee.freigeld.utils.KeyValueBucket
 import inn.ocsf.bee.freigeld.utils.KeyValueStorage
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import org.slf4j.LoggerFactory
+import org.springframework.context.annotation.Lazy
 import org.springframework.scheduling.annotation.Scheduled
 import org.springframework.stereotype.Service
 import org.springframework.transaction.annotation.Transactional
@@ -45,6 +47,10 @@ class GlobalEmitterImpl : GlobalEmitter {
     @Inject
     private lateinit var kvStore: KeyValueStorage
 
+    @Inject
+    @Lazy
+    private var centralBank: CentralBank? = null
+
     private val globalId = UUID.fromString("a671cdca-782a-4caf-8aad-056f6b62d822")
 
     private val listeners = mutableListOf<Consumer<EmitterEvent>>()
@@ -79,15 +85,18 @@ class GlobalEmitterImpl : GlobalEmitter {
 
     @PostConstruct
     fun init() {
-        //coinRepo.deleteAll()
-        ticketService.listen(emitterTicketChannel) { e ->
-            when (e) {
-                is EmitterRecalculationEvent -> {
-                    calc()
-                    log.info("emitter recalculated ${e.dt}")
-                    true
+        GlobalScope.launch {
+            delay(10 * 1000L)
+            if (centralBank?.id == null) throw RuntimeException("bank not ready")
+            ticketService.listen(emitterTicketChannel) { e ->
+                when (e) {
+                    is EmitterRecalculationEvent -> {
+                        calc()
+                        log.info("emitter recalculated ${e.dt}")
+                        true
+                    }
+                    else -> throw RuntimeException("wrong event type ${e::class.java}")
                 }
-                else -> throw RuntimeException("wrong event type ${e::class.java}")
             }
         }
     }

+ 1 - 1
src/main/kotlin/inn/ocsf/bee/freigeld/core/demo/DemoInMem.kt

@@ -73,7 +73,7 @@ class DemoInMem {
         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).thenRun({ log.info("exchange ok") }).exceptionally({ log.error("exchange error ${it.message}"); null })
+        bank.exchange(dir.second, dir.first, x, null).thenRun({ log.info("exchange ok") }).exceptionally({ log.error("exchange error ${it.message}"); null })
     }
 
     //@Scheduled(initialDelay = 30 * 1000L, fixedDelay = 2000L)

+ 16 - 5
src/main/kotlin/inn/ocsf/bee/freigeld/serve/rest/PersonController.kt

@@ -57,12 +57,12 @@ class PersonController {
     private val log = LoggerFactory.getLogger(javaClass)
 
 
+
     @RequestMapping("/api/old/person/{personId}/get-credit-link/{linkId}", method = [RequestMethod.GET])
     fun getCreditLink(@PathVariable("personId") personId: UUID, @PathVariable("linkId") linkId: String): ResponseEntity<Map<String, Any?>> {
         val link = linkRepo.findById(linkId).filter { it is PublicLinkDebitAccount }.map { it as PublicLinkDebitAccount }.orElse(null)
         if (link != null) {
-            val dt = LocalDateTime.from((link.dt ?: Date()).toInstant().atZone(ZoneId.systemDefault()))
-            if (link.limited != true || !(dt.plusHours(link.time!!).isBefore(LocalDateTime.now()))) {
+            if (!link.isWasted()) {
                 return ResponseEntity.ok(mapOf("ok" to true, "amount" to link.amount))
             } else {
                 return ResponseEntity(HttpStatus.GONE)
@@ -120,7 +120,7 @@ class PersonController {
     fun giveMoney(@PathVariable("personId") personId: UUID): ResponseEntity<Map<String, Any?>> {
         val account = bank.getAccounts(personId).randomOrNull()
         if (account != null) {
-            bank.exchange(account, bank.getSelfAccount(), (100 * Math.random()).toLong()).get(5, TimeUnit.SECONDS)
+            bank.exchange(account, bank.getSelfAccount(), (100 * Math.random()).toLong(), BankExchangeDetails.BankExchangeDetailsBuilder.aBankExchangeDetails().withDescription("basic income").build()).get(5, TimeUnit.SECONDS)
         }
         return ResponseEntity.ok(mapOf())
     }
@@ -137,11 +137,16 @@ class PersonController {
         val fromAccount = bank.getAccounts(personId).first { it.id == accountId }
         val link = linkRepo.findById(linkId).filter { it is PublicLinkDebitAccount }.map { it as PublicLinkDebitAccount }.orElse(null)
         val ret = if (fromAccount != null) {
-            if (link != null) {
+            if (link != null && !link.isWasted()) {
                 val toAccount = bank.getAccount(link.accountId!!)
                 if (toAccount != null) {
                     if (req.amount != null && req.amount > 0) {
-                        bank.exchange(toAccount, fromAccount, req.amount).get(5, TimeUnit.SECONDS)
+                        val e = bank.exchange(toAccount, fromAccount, req.amount, BankExchangeDetails.BankExchangeDetailsBuilder.aBankExchangeDetails().withDescription("by link ${linkId}").withExtraFields(mapOf("linkId" to linkId)).build()).get(5, TimeUnit.SECONDS)
+                        if (link.once == true) {
+                            link.status = PublicLinkStatus.done
+                            linkRepo.save(link)
+                        }
+                        e
                     } else {
                         null
                     }
@@ -189,6 +194,12 @@ class PublicLinkDebitAccount() : PublicLink() {
         this.limited = limited
         this.time = time
         this.dt = Date()
+        this.status = PublicLinkStatus.active
+    }
+
+    fun isWasted(): Boolean {
+        val dt = LocalDateTime.from((this.dt ?: Date()).toInstant().atZone(ZoneId.systemDefault()))
+        return (this.once != true || this.status != PublicLinkStatus.done) && (this.limited != true || !(dt.plusHours(this.time!!).isBefore(LocalDateTime.now())))
     }
 }