编写新交易流程测试代码
diff --git a/build.gradle b/build.gradle
index 350290f..5af099d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -38,6 +38,9 @@
 
     implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
 
+    implementation 'com.github.chenhaiyangs:rpc-tcc-transaction-spring:1.2.0'
+    implementation 'com.github.chenhaiyangs:rpc-tcc-transaction-api:1.1.0'
+    implementation 'com.github.chenhaiyangs:rpc-tcc-transaction-core:1.2.0'
     implementation 'org.postgresql:postgresql:42.2.5'
     implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
     implementation 'com.jcabi:jcabi-manifests:1.1'
diff --git a/src/main/java/com/supwisdom/dlpay/api/dao/AccountDao.java b/src/main/java/com/supwisdom/dlpay/api/dao/AccountDao.java
index 8d3130d..733a6c2 100644
--- a/src/main/java/com/supwisdom/dlpay/api/dao/AccountDao.java
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/AccountDao.java
@@ -34,9 +34,5 @@
   @Query("select a from TAccount a where a.userid = ?1 and a.subjno=?2")
   TAccount findByUseridAndSubjno(String userid, String subjno);
 
-  @Modifying
-  @Query("update TAccount t set t.availbal=t.availbal-?3 , t.balance=t.balance-?3 where t.accno=?1 and t.availbal=?2")
-  int updateAccountBalance(String accno, Double availbal, Double amount);
-
   Page<TAccount> findAllByAccnameContaining(String accname, Pageable pageable);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/api/repositories/TAccountRepository.java b/src/main/java/com/supwisdom/dlpay/api/repositories/TAccountRepository.java
index d999a27..222391f 100644
--- a/src/main/java/com/supwisdom/dlpay/api/repositories/TAccountRepository.java
+++ b/src/main/java/com/supwisdom/dlpay/api/repositories/TAccountRepository.java
@@ -1,5 +1,12 @@
 package com.supwisdom.dlpay.api.repositories;
 
+import com.supwisdom.dlpay.api.domain.TAccount;
+import com.supwisdom.dlpay.api.domain.TPersondtl;
+
 public interface TAccountRepository {
-  int updateAccountBalance(String accno, Double avaibal, Double amount);
+  int recalcAccountBalance(String accno, Double avaibal, Double amount, boolean overdraft);
+
+  int recalcAccountBalance(TAccount account, Double amount, boolean overdraft);
+
+  int recalcAccountBalance(TPersondtl dtl, Double amount, boolean overdraft);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/api/repositories/TShopaccRepository.java b/src/main/java/com/supwisdom/dlpay/api/repositories/TShopaccRepository.java
new file mode 100644
index 0000000..291bd1c
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/repositories/TShopaccRepository.java
@@ -0,0 +1,7 @@
+package com.supwisdom.dlpay.api.repositories;
+
+import com.supwisdom.dlpay.api.domain.TShopdtl;
+
+public interface TShopaccRepository {
+  int recalcShopBalance(TShopdtl dtl, Double amount, boolean overdraft);
+}
diff --git a/src/main/java/com/supwisdom/dlpay/api/repositories/impl/TAccountRepositoryImpl.java b/src/main/java/com/supwisdom/dlpay/api/repositories/impl/TAccountRepositoryImpl.java
deleted file mode 100644
index 029e85f..0000000
--- a/src/main/java/com/supwisdom/dlpay/api/repositories/impl/TAccountRepositoryImpl.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.supwisdom.dlpay.api.repositories.impl;
-
-import com.supwisdom.dlpay.api.domain.TAccount;
-import com.supwisdom.dlpay.api.repositories.TAccountRepository;
-import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Transactional;
-
-import javax.persistence.EntityManager;
-import javax.persistence.PersistenceContext;
-import javax.persistence.Query;
-
-@Repository
-@Transactional
-public class TAccountRepositoryImpl implements TAccountRepository {
-
-  @PersistenceContext
-  private EntityManager em;
-
-  @Override
-  public int updateAccountBalance(String accno, Double avaibal, Double amount) {
-
-    TAccount account = em.find(TAccount.class, accno);
-    if (account == null) {
-      return 0;
-    }
-
-
-
-    return 0;
-  }
-}
diff --git a/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt b/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
index 580ee13..941e6b1 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
@@ -1,5 +1,6 @@
 package com.supwisdom.dlpay
 
+import com.fasterxml.jackson.databind.ser.std.StringSerializer
 import io.lettuce.core.ReadFrom
 import org.springframework.beans.factory.annotation.Value
 import org.springframework.boot.autoconfigure.SpringBootApplication
@@ -61,6 +62,13 @@
         template.setConnectionFactory(connectionFactory)
         return template
     }
+
+    @Bean
+    fun longValueRedisTemplate(connectionFactory: RedisConnectionFactory): RedisTemplate<String, Long> {
+        val template = RedisTemplate<String, Long>()
+        template.keySerializer = StringRedisSerializer()
+        return template
+    }
 }
 
 
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/repositories/repository_impl.kt b/src/main/kotlin/com/supwisdom/dlpay/api/repositories/repository_impl.kt
new file mode 100644
index 0000000..5c557d3
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/repositories/repository_impl.kt
@@ -0,0 +1,72 @@
+package com.supwisdom.dlpay.api.repositories
+
+import com.supwisdom.dlpay.api.domain.TAccount
+import com.supwisdom.dlpay.api.domain.TPersondtl
+import com.supwisdom.dlpay.api.domain.TShopdtl
+import com.supwisdom.dlpay.exception.TransactionProcessException
+import com.supwisdom.dlpay.framework.domain.TShopacc
+import com.supwisdom.dlpay.framework.util.TradeErrorCode
+import org.springframework.stereotype.Repository
+import org.springframework.transaction.annotation.Transactional
+import javax.persistence.EntityManager
+import javax.persistence.PersistenceContext
+
+@Repository
+@Transactional
+class TAccountRepositoryImpl : TAccountRepository {
+
+    @PersistenceContext
+    private lateinit var entityManager: EntityManager
+
+    override fun recalcAccountBalance(accno: String, avaibal: Double, amount: Double, overdraft: Boolean): Int {
+        val account = entityManager.find(TAccount::class.java, accno) ?: return 0
+        if (avaibal.compareTo(account.availbal!!) != 0) {
+            throw TransactionProcessException(TradeErrorCode.ACCOUNT_TRADE_BUSY,
+                    "账户余额已被更新")
+        }
+
+        //1. 从 redis 读取账户余额
+        //2. 没有记录时,读取数据库余额并存入 redis ,如果返回已存在,读取redis 余额
+        //3. 扣除 redis 中余额
+        val query = entityManager.createQuery("update TAccount a set a.availbal=a.availbal+?1, a.balance=a.balance+?1 " +
+                "where a.accno=?2 and a.availbal=?3")
+                ?: throw TransactionProcessException(TradeErrorCode.BUSINESS_DEAL_ERROR, "更新个人账户余额错误")
+
+        query.setParameter(1, amount)
+                .setParameter(2, accno)
+                .setParameter(3, avaibal)
+        return query.executeUpdate()
+    }
+
+    override fun recalcAccountBalance(account: TAccount, amount: Double, overdraft: Boolean): Int {
+        return recalcAccountBalance(account.accno, account.availbal, amount, overdraft)
+    }
+
+    override fun recalcAccountBalance(dtl: TPersondtl, amount: Double, overdraft: Boolean): Int {
+        return entityManager.find(TAccount::class.java, dtl.accountNo)?.let {
+            recalcAccountBalance(it, amount, overdraft)
+        } ?: throw TransactionProcessException(TradeErrorCode.BUSINESS_DEAL_ERROR,
+                "交易流水<${dtl.refno}>未找到指定个人账户")
+    }
+}
+
+@Repository
+@Transactional
+class ShopaccRepositoryImpl : TShopaccRepository {
+
+    @PersistenceContext
+    private lateinit var entityManager: EntityManager
+
+    override fun recalcShopBalance(dtl: TShopdtl, amount: Double, overdraft: Boolean): Int {
+        val shopacc = entityManager.find(TShopacc::class.java, dtl.shopaccno)
+                ?: throw TransactionProcessException(TradeErrorCode.ACCOUNT_NOT_EXISTS,
+                        "交易流水<${dtl.refno}>商户账户不存在")
+
+        val query = entityManager.createQuery("update TShopacc c set c.balance=c.balance-?1" +
+                " where c.shopaccno=?2 and c.balance=?3")
+        query.setParameter(1, amount)
+                .setParameter(2, dtl.shopaccno)
+                .setParameter(3, shopacc.balance)
+        return query.executeUpdate()
+    }
+}
\ 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 4359f7f..ebf7118 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
@@ -4,15 +4,26 @@
 import com.supwisdom.dlpay.api.TransactionResult
 import com.supwisdom.dlpay.api.dao.*
 import com.supwisdom.dlpay.api.domain.*
+import com.supwisdom.dlpay.api.repositories.ShopaccRepositoryImpl
+import com.supwisdom.dlpay.api.repositories.TAccountRepositoryImpl
+import com.supwisdom.dlpay.api.service.PersonAccountService
 import com.supwisdom.dlpay.api.service.TransactionService
 import com.supwisdom.dlpay.exception.TransactionProcessException
 import com.supwisdom.dlpay.framework.util.Subject
 import com.supwisdom.dlpay.framework.util.TradeDict
 import com.supwisdom.dlpay.framework.util.TradeErrorCode
 import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.data.repository.findByIdOrNull
 import org.springframework.stereotype.Service
 import java.sql.SQLException
+import javax.transaction.Transactional
+
+
+open class PersonAccountServiceImpl : PersonAccountService {
+    @Transactional
+    override fun recalcBalance(account: TAccount, amount: Double) {
+        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+    }
+}
 
 @Service
 class TransactionServiceImpl : TransactionService {
@@ -39,6 +50,12 @@
     @Autowired
     lateinit var debitCreditDtlDao: DebitCreditDtlDao
 
+    @Autowired
+    lateinit var accountRepository: TAccountRepositoryImpl
+
+    @Autowired
+    lateinit var shopaccRepository: ShopaccRepositoryImpl
+
     private fun builderRecords(builder: TransactionBuilder, status: String): TransactionResult {
         // 记录三方的交易流水(个人,商户,科目)
         try {
@@ -250,9 +267,6 @@
         transactionMainDao.save(transaction)
     }
 
-    private fun decreseAccountBalance(account: TAccount, amount: Double) {
-
-    }
 
     override fun success(refno: String) {
         val transaction = transactionMainDao.findByRefnoForUpdate(refno)
@@ -266,12 +280,16 @@
         if (transaction.person) {
             // update account balance
             transaction.personDtl?.let {
-                accoutDao.updateAccountBalance("", 0.0, transaction.personDtl.amount)
+                accountRepository.recalcAccountBalance(it, it.amount, false)
             } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
-                    "个人<${transaction.personDtl.userid}>不存在")
+                    "个人流水<${transaction.refno}>不存在")
         }
         if (transaction.shop) {
             // update shop balance
+            transaction.shopDtl?.let {
+                shopaccRepository.recalcShopBalance(it, it.amount, false)
+            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
+                    "商户流水<${transaction.refno}>不存在")
         }
 
         if (transaction.subject) {
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 b4407c1..8872f33 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
@@ -2,6 +2,7 @@
 
 import com.supwisdom.dlpay.api.TransactionBuilder
 import com.supwisdom.dlpay.api.TransactionResult
+import com.supwisdom.dlpay.api.domain.TAccount
 import org.springframework.transaction.annotation.Propagation
 import org.springframework.transaction.annotation.Transactional
 
@@ -20,4 +21,8 @@
 
     @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
     fun success(refno: String)
+}
+
+interface PersonAccountService {
+    fun recalcBalance(account: TAccount, amount: Double)
 }
\ No newline at end of file