增加了商户余额异步更新机制
diff --git a/src/main/java/com/supwisdom/dlpay/api/dao/ShopdtlDao.java b/src/main/java/com/supwisdom/dlpay/api/dao/ShopdtlDao.java
index 611d3cb..0e49580 100644
--- a/src/main/java/com/supwisdom/dlpay/api/dao/ShopdtlDao.java
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/ShopdtlDao.java
@@ -7,9 +7,13 @@
 import org.springframework.stereotype.Repository;
 
 import javax.persistence.LockModeType;
+import java.util.List;
 
 @Repository
 public interface ShopdtlDao extends JpaRepository<TShopdtl, String>, JpaSpecificationExecutor<TShopdtl> {
   @Lock(LockModeType.OPTIMISTIC)
   TShopdtl findTShopdtlByRefno(String refno);
+
+  @Lock(LockModeType.OPTIMISTIC)
+  List<TShopdtl> findAllByStatusAndUpdateBala(String status, Boolean flag);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/api/repositories/ShopaccService.java b/src/main/java/com/supwisdom/dlpay/api/repositories/ShopaccService.java
index cea6e08..2d8ebcf 100644
--- a/src/main/java/com/supwisdom/dlpay/api/repositories/ShopaccService.java
+++ b/src/main/java/com/supwisdom/dlpay/api/repositories/ShopaccService.java
@@ -4,7 +4,15 @@
 import org.springframework.transaction.annotation.Isolation;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.List;
+
 public interface ShopaccService {
   @Transactional(isolation = Isolation.READ_COMMITTED)
   void recalcShopBalance(TShopdtl dtl, Double amount, boolean overdraft);
+
+  @Transactional(isolation = Isolation.READ_COMMITTED)
+  void recalcShopBalance(String refno, boolean overdraft);
+
+  @Transactional
+  List<TShopdtl> findUnupdatedShopDtl(int maxCount);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/framework/domain/TShopacc.java b/src/main/java/com/supwisdom/dlpay/framework/domain/TShopacc.java
index 9d8b8b4..b7df8d2 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/domain/TShopacc.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/domain/TShopacc.java
@@ -1,9 +1,6 @@
 package com.supwisdom.dlpay.framework.domain;
 
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import javax.persistence.*;
 import java.sql.Timestamp;
 
 @Entity
@@ -34,9 +31,7 @@
   @Column(name = "BALANCE", length = 15, precision = 2)
   private Double balance = 0.0;
 
-  @Column(name = "VERSION")
-  private Long version = 1L;
-
+  @Version
   @Column(name = "LASTUPDATE")
   private Timestamp lastUpdate = new Timestamp(System.currentTimeMillis());
 
@@ -107,10 +102,6 @@
     return balance;
   }
 
-  public Long getVersion() {
-    return this.version;
-  }
-
   public String getMac() {
     return mac;
   }
@@ -122,7 +113,6 @@
   public void incrOrDecrBalance(Double amount) {
     this.balance += amount;
     this.lastUpdate = new Timestamp(System.currentTimeMillis());
-    this.version++;
   }
 
 }
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/TradeErrorCode.java b/src/main/java/com/supwisdom/dlpay/framework/util/TradeErrorCode.java
index 71df139..08bf26f 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/TradeErrorCode.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/TradeErrorCode.java
@@ -36,6 +36,11 @@
   public static final int TRANSACTION_IS_FINISHED = 10005;
 
   /**
+   * 交易状态错误,正在处理
+   */
+  public static final int TRANSACTION_HAS_BEEN_PROCESS = 100030;
+
+  /**
    * 交易已冲正
    */
   public static final int TRANSACTION_HAS_CANCELED = 10006;
diff --git a/src/main/kotlin/com/supwisdom/dlpay/account_process_async.kt b/src/main/kotlin/com/supwisdom/dlpay/account_process_async.kt
index a1bd2f0..3e3918f 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/account_process_async.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/account_process_async.kt
@@ -13,6 +13,8 @@
 import org.springframework.scheduling.annotation.EnableAsync
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
 import org.springframework.stereotype.Component
+import org.springframework.stereotype.Service
+import org.springframework.transaction.annotation.Transactional
 import java.lang.reflect.Method
 import java.util.concurrent.Executor
 
@@ -46,25 +48,11 @@
     private val logger = KotlinLogging.logger { }
 
     @Autowired
-    private lateinit var transactionMainDao: TransactionMainDao
-
-    @Autowired
-    lateinit var shopdltDao: ShopdtlDao
-
-    @Autowired
     private lateinit var shopaccService: ShopaccService
 
     @Async("shopAccBalanceUpdater")
+    @Transactional
     fun updateShopBalance(shopdtlRefno: String) {
-        shopdltDao.findTShopdtlByRefno(shopdtlRefno)?.also {
-            if (it.updateBala) {
-                logger.warn { "shop balance update refno <$shopdtlRefno> has been updated" }
-                return
-            }
-        }?.also {
-            shopaccService.recalcShopBalance(it, it.amount, true)
-            it.updateBala = true
-            shopdltDao.save(it)
-        } ?: logger.warn { "shop balance updater refno<$shopdtlRefno> not found" }
+        shopaccService.recalcShopBalance(shopdtlRefno, true)
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt b/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
index 1214889..12cb97d 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
@@ -7,10 +7,7 @@
 import com.supwisdom.dlpay.api.bean.*
 import com.supwisdom.dlpay.api.dao.TransactionMainDao
 import com.supwisdom.dlpay.api.domain.TAccount
-import com.supwisdom.dlpay.api.service.AccountUtilServcie
-import com.supwisdom.dlpay.api.service.ConsumePayService
-import com.supwisdom.dlpay.api.service.TransactionService
-import com.supwisdom.dlpay.api.service.UserService
+import com.supwisdom.dlpay.api.service.*
 import com.supwisdom.dlpay.exception.RequestParamCheckException
 import com.supwisdom.dlpay.exception.TransactionCheckException
 import com.supwisdom.dlpay.exception.TransactionException
@@ -47,10 +44,7 @@
     lateinit var transactionMainDao: TransactionMainDao
 
     @Autowired
-    lateinit var transactionService: TransactionService
-
-    @Autowired
-    private lateinit var shopAccBalanceAsyncTask: ShopAccBalanceAsyncTask
+    lateinit var transactionService: TransactionServiceProxy
 
     /**
      * 流水结果查询统一接口
@@ -181,7 +175,7 @@
                     }
                 }.init(transactionService)
 
-                transactionService.success(dtl.refno, "")
+                transactionService.success(dtl.refno)
 
                 return ResponseEntity.ok(ResponseBodyBuilder.create()
                         .data("refno", dtl.refno)
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt b/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt
new file mode 100644
index 0000000..a0379df
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt
@@ -0,0 +1,27 @@
+package com.supwisdom.dlpay.api
+
+import com.supwisdom.dlpay.api.dao.ShopdtlDao
+import com.supwisdom.dlpay.api.domain.TShopdtl
+import com.supwisdom.dlpay.api.repositories.ShopaccService
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.scheduling.annotation.Scheduled
+import org.springframework.stereotype.Service
+import org.springframework.transaction.annotation.Transactional
+
+@Service
+class MySchedulerTask {
+    @Autowired
+    private lateinit var shopaccService: ShopaccService
+
+    fun doShopBlanceUpdate(dtl: TShopdtl) {
+        shopaccService.recalcShopBalance(dtl.refno, true)
+    }
+
+    @Scheduled(fixedRate = 5000)
+    @Transactional
+    fun dealShopUnupdatedDtl() {
+        shopaccService.findUnupdatedShopDtl(100).forEach {
+            doShopBlanceUpdate(it)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
index b7b4604..6e72082 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
@@ -8,6 +8,7 @@
 import com.supwisdom.dlpay.framework.service.SystemUtilService
 import com.supwisdom.dlpay.framework.util.DateUtil
 import com.supwisdom.dlpay.framework.util.TradeDict
+import com.supwisdom.dlpay.framework.util.TradeDict.*
 import com.supwisdom.dlpay.framework.util.TradeErrorCode
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.stereotype.Repository
@@ -68,7 +69,7 @@
                         account.lastdayTransamt = 0.0
                         account.lastdayDpsamt = 0.0
                     }
-                    if (dtl.tradeflag == TradeDict.TRADE_FLAG_OUT) {
+                    if (dtl.tradeflag == TRADE_FLAG_OUT) {
                         account.lastdayTransamt += amount
                     } else {
                         account.lastdayDpsamt += amount
@@ -93,14 +94,32 @@
     override fun recalcShopBalance(dtl: TShopdtl, amount: Double, overdraft: Boolean) {
         val shopacc = entityManager.find(TShopacc::class.java, dtl.shopaccno, LockModeType.OPTIMISTIC)
                 ?: throw TransactionProcessException(TradeErrorCode.ACCOUNT_NOT_EXISTS,
-                        "交易流水<${dtl.refno}>商户账户不存在")
+                        "商户账户<${dtl.shopaccno}>不存在")
 
         shopacc.balance += amount
         if (shopacc.balance < 0.0 && !overdraft) {
             throw TransactionProcessException(TradeErrorCode.SHORT_BALANCE_ERROR,
                     "商户账户余额不足")
         }
-
         entityManager.persist(shopacc)
     }
+
+    override fun recalcShopBalance(refno: String, overdraft: Boolean) {
+        val shopdtl = entityManager.find(TShopdtl::class.java, refno, LockModeType.PESSIMISTIC_WRITE)
+                ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
+                        "商户流水<$refno>不存在")
+        if (shopdtl.updateBala) {
+            return
+        }
+        recalcShopBalance(shopdtl, shopdtl.amount, overdraft)
+        shopdtl.updateBala = true
+        entityManager.persist(shopdtl)
+    }
+
+    override fun findUnupdatedShopDtl(maxCount: Int): List<TShopdtl> {
+        return entityManager.createQuery("""
+            SELECT p FROM TShopdtl p
+            WHERE p.status='$DTL_STATUS_SUCCESS' and p.updateBala=false
+            ORDER BY p.refno""", TShopdtl::class.java).setMaxResults(maxCount).resultList
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
index 05b57b2..3c8b3a6 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
@@ -17,8 +17,11 @@
 import com.supwisdom.dlpay.framework.util.TradeErrorCode
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.stereotype.Service
+import org.springframework.transaction.annotation.Propagation
+import org.springframework.transaction.annotation.Transactional
 import java.sql.SQLException
 import java.sql.Timestamp
+import kotlin.math.absoluteValue
 
 
 @Service
@@ -44,9 +47,6 @@
     @Autowired
     private lateinit var sourceTypeService: SourceTypeService
 
-    @Autowired
-    private lateinit var shopAccBalanceAsyncTask: ShopAccBalanceAsyncTask
-
     private fun preCheck(builder: TransactionBuilder) {
         builder.preCheck()
 
@@ -100,8 +100,7 @@
                     remark = builder.person().remark
                     this.status = status
                 }.also {
-                    // save persondtl
-                    persondtlDao.save(it)
+                    //                    persondtlDao.save(it)
                     transaction.personDtl = it
                     transaction.person = true
                 }
@@ -130,6 +129,7 @@
                     this.oppositeAccName = builder.shop().opposite.getAccountName()
                     this.remark = builder.shop().remark
                     this.reverseFlag = TradeDict.REVERSE_FLAG_NONE
+                    this.updateBala = false
                     this.status = status
                 }.also {
                     // save shopdtl
@@ -315,14 +315,6 @@
     }
 
     override fun success(refno: String, remark: String): TTransactionMain {
-        val transaction = successOnAccount(refno, remark)
-        if (transaction.status == TradeDict.DTL_STATUS_SUCCESS) {
-            shopAccBalanceAsyncTask.updateShopBalance(transaction.refno)
-        }
-        return transaction
-    }
-
-    override fun successOnAccount(refno: String, remark: String): TTransactionMain {
         val transaction = transactionMainDao.findByRefnoForUpdate(refno)
                 ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>参考号错误")
 
@@ -377,7 +369,7 @@
             val amount = transaction.sumAmountByAccno(
                     transaction.personDtl.accountNo, Subject.SUBJNO_PERSONAL_DEPOSIT,
                     PERSON_BALANCE_FLAG, "both")
-            if (amount.compareTo(0.0) != 0) {
+            if (amount.absoluteValue.compareTo(0.0) != 0) {
                 transaction.personDtl?.let {
                     accountService.recalcAccountBalance(it, amount, overdraft)
                     transaction.personDtl.accdate = transaction.accdate
@@ -389,16 +381,13 @@
         }
         if (transaction.shop) {
             // update shop balance
-            val amount = transaction.sumAmountByAccno(
-                    transaction.shopDtl.shopaccno, Subject.SUBJNO_MACHANT_INCOME,
-                    SHOP_BALANCE_FLAG, "both")
-            if (amount.compareTo(0.0) != 0) {
-//                transaction.shopDtl?.let {
-//                    shopaccService.recalcShopBalance(it, amount, overdraft)
-//                } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
-//                        "商户流水<${transaction.refno}>不存在")
-//                shopAccBalanceAsyncTask.updateShopBalance(transaction.shopDtl.refno)
-            }
+//            val amount = transaction.sumAmountByAccno(
+//                    transaction.shopDtl.shopaccno, Subject.SUBJNO_MACHANT_INCOME,
+//                    SHOP_BALANCE_FLAG, "both")
+//            if (amount.absoluteValue.compareTo(0.0) != 0) {
+//
+//            }
+            transaction.shopDtl.updateBala = false
             transaction.shopDtl.status = TradeDict.DTL_STATUS_SUCCESS
             transaction.shopDtl.accdate = transaction.accdate
             transaction.shopDtl.remark = remark
@@ -428,4 +417,4 @@
         transactionMainDao.save(transaction)
         return transaction
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt
index c2b942a..223a12b 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt
@@ -1,7 +1,10 @@
 package com.supwisdom.dlpay.api.service
 
+import com.supwisdom.dlpay.ShopAccBalanceAsyncTask
 import com.supwisdom.dlpay.api.TransactionBuilder
 import com.supwisdom.dlpay.api.domain.TTransactionMain
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Component
 import org.springframework.transaction.annotation.Propagation
 import org.springframework.transaction.annotation.Transactional
 
@@ -22,9 +25,9 @@
     fun fail(refno: String, remark: String): TTransactionMain
 
     @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
-    fun successOnAccount(refno: String, remark: String): TTransactionMain
-
     fun success(refno: String, remark: String): TTransactionMain
+
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
     fun success(refno: String): TTransactionMain
 
     // 撤销接口冲正类接口
@@ -58,3 +61,45 @@
     @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
     fun repair(refno: String, remark: String): TTransactionMain
 }
+
+@Component
+class TransactionServiceProxy {
+    @Autowired
+    private lateinit var transactionService: TransactionService
+
+    @Autowired
+    private lateinit var shopAccBalanceAsyncTask: ShopAccBalanceAsyncTask
+
+
+    fun init(builder: TransactionBuilder): TTransactionMain {
+        return transactionService.init(builder)
+    }
+
+    fun wip(refno: String): TTransactionMain {
+        return transactionService.wip(refno)
+    }
+
+    fun wip(builder: TransactionBuilder): TTransactionMain {
+        return transactionService.wip(builder)
+    }
+
+    fun fail(refno: String): TTransactionMain {
+        return transactionService.fail(refno)
+    }
+
+    fun fail(refno: String, remark: String): TTransactionMain {
+        return transactionService.fail(refno, remark)
+    }
+
+    fun success(refno: String, remark: String): TTransactionMain {
+        return transactionService.success(refno, remark).also {
+            if (it.shop) {
+                shopAccBalanceAsyncTask.updateShopBalance(it.refno)
+            }
+        }
+    }
+
+    fun success(refno: String): TTransactionMain {
+        return success(refno, "")
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt b/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt
index 9dc29ac..74f77fd 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt
@@ -3,6 +3,7 @@
 import com.supwisdom.dlpay.api.domain.TAccount
 import com.supwisdom.dlpay.api.domain.TTransactionMain
 import com.supwisdom.dlpay.api.service.TransactionService
+import com.supwisdom.dlpay.api.service.TransactionServiceProxy
 import com.supwisdom.dlpay.exception.TransactionCheckException
 import com.supwisdom.dlpay.framework.domain.TShopacc
 import com.supwisdom.dlpay.framework.domain.TSubject
@@ -337,19 +338,19 @@
         }
     }
 
-    fun init(transactionService: TransactionService): TTransactionMain {
+    fun init(transactionService: TransactionServiceProxy): TTransactionMain {
         return transactionService.init(this)
     }
 
-    fun wip(transactionService: TransactionService): TTransactionMain {
+    fun wip(transactionService: TransactionServiceProxy): TTransactionMain {
         return transactionService.wip(this)
     }
 
-    fun cancel(transactionService: TransactionService, originRefno: String): TTransactionMain {
+    fun cancel(transactionService: TransactionServiceProxy, originRefno: String): TTransactionMain {
         TODO("not implement")
     }
 
-    fun reverse(transactionService: TransactionService, originRefno: String, amount: Double): TTransactionMain {
+    fun reverse(transactionService: TransactionServiceProxy, originRefno: String, amount: Double): TTransactionMain {
         TODO("not implement")
     }
 }