初步测试交易记账流程
diff --git a/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java b/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java
index 3713fe4..376b528 100644
--- a/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java
+++ b/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java
@@ -77,12 +77,6 @@
   @Column(name = "REMARK", length = 240)
   private String remark;
 
-  @Column(name = "CREATETIME", length = 14)
-  private String createtime;  //创建时间
-
-  @Column(name = "ENDTIME", length = 14)
-  private String endtime;  //支付结束时间
-
   public String getRefno() {
     return refno;
   }
@@ -235,22 +229,6 @@
     this.remark = remark;
   }
 
-  public String getCreatetime() {
-    return createtime;
-  }
-
-  public void setCreatetime(String createtime) {
-    this.createtime = createtime;
-  }
-
-  public String getEndtime() {
-    return endtime;
-  }
-
-  public void setEndtime(String endtime) {
-    this.endtime = endtime;
-  }
-
   public String getUserName() {
     return userName;
   }
diff --git a/src/main/java/com/supwisdom/dlpay/api/domain/TShopdtl.java b/src/main/java/com/supwisdom/dlpay/api/domain/TShopdtl.java
index 2078c99..14dcb5d 100644
--- a/src/main/java/com/supwisdom/dlpay/api/domain/TShopdtl.java
+++ b/src/main/java/com/supwisdom/dlpay/api/domain/TShopdtl.java
@@ -39,6 +39,9 @@
   @Column(name = "payinfo", length = 200)
   private String payInfo;
 
+  @Column(name = "tradeflag", length = 6)
+  private String tradeflag;
+
   @Column(name = "status", length = 20)
   private String status;
 
@@ -151,4 +154,12 @@
   public void setTradeCode(Integer tradeCode) {
     this.tradeCode = tradeCode;
   }
+
+  public String getTradeflag() {
+    return tradeflag;
+  }
+
+  public void setTradeflag(String tradeflag) {
+    this.tradeflag = tradeflag;
+  }
 }
diff --git a/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionMain.java b/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionMain.java
index c60c621..6c7507d 100644
--- a/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionMain.java
+++ b/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionMain.java
@@ -4,6 +4,7 @@
 
 import javax.persistence.*;
 
+import java.sql.Timestamp;
 import java.util.List;
 
 import static javax.persistence.FetchType.LAZY;
@@ -39,22 +40,28 @@
   @Column(name = "outid", length = 60)
   private String outId;
 
+  @Column(name = "create_time")
+  private Timestamp createTime;
+
+  @Column(name = "end_time")
+  private Timestamp endTime;
+
   @Column(name = "reverse_flag", nullable = false, length = 10)
   private String reverseFlag = "none";
 
-  @OneToOne(targetEntity = TPersondtl.class, fetch = LAZY)
+  @OneToOne(targetEntity = TPersondtl.class, fetch = LAZY, cascade = CascadeType.ALL)
   @JoinColumn(name = "refno", referencedColumnName = "refno")
   private TPersondtl personDtl;
 
-  @OneToOne(targetEntity = TShopdtl.class, fetch = LAZY)
+  @OneToOne(targetEntity = TShopdtl.class, fetch = LAZY, cascade = CascadeType.ALL)
   @JoinColumn(name = "refno", referencedColumnName = "refno")
   private TShopdtl shopDtl;
 
-  @OneToOne(targetEntity = TSubjectdtl.class, fetch = LAZY)
+  @OneToOne(targetEntity = TSubjectdtl.class, fetch = LAZY, cascade = CascadeType.ALL)
   @JoinColumn(name = "refno", referencedColumnName = "refno")
   private TSubjectdtl subjectDtl;
 
-  @OneToMany(targetEntity = TDebitCreditDtl.class, fetch = LAZY)
+  @OneToMany(targetEntity = TDebitCreditDtl.class, fetch = LAZY, cascade = CascadeType.ALL)
   @JoinColumn(name = "refno", referencedColumnName = "refno")
   private List<TDebitCreditDtl> details;
 
@@ -162,4 +169,20 @@
   public void setDetails(List<TDebitCreditDtl> details) {
     this.details = details;
   }
+
+  public Timestamp getCreateTime() {
+    return createTime;
+  }
+
+  public void setCreateTime(Timestamp createTime) {
+    this.createTime = createTime;
+  }
+
+  public Timestamp getEndTime() {
+    return endTime;
+  }
+
+  public void setEndTime(Timestamp endTime) {
+    this.endTime = endTime;
+  }
 }
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
index ed6db07..c786dea 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
@@ -1,5 +1,7 @@
 package com.supwisdom.dlpay.framework.util;
 
+import org.jetbrains.annotations.Nullable;
+
 public class TradeDict {
   /**
    * 状态:
@@ -34,8 +36,8 @@
   public static final String DTL_STATUS_NONE = "none";
 
 
-  public static final String TRADE_FLAG_DEPOSIT = "deposit";
-  public static final String TRADE_FLAG_PAY = "pay";
+  public static final String TRADE_FLAG_OUT = "out";
+  public static final String TRADE_FLAG_IN = "in";
   /**
    * 交易借方
    */
@@ -65,4 +67,14 @@
 
   public static final String PAYTYPE_RECHARGE_COUPON = "coupon";  // 充值优惠
   public static final String PAYTYPE_RECHARGE_SERVICEFEE = "servicefee"; //收服务费
+
+  /**
+   * reverse flag
+   * - none : 无
+   * - cancel : 冲正
+   * - reverse : 手工撤销
+   */
+  public static final String REVERSE_FLAG_NONE = "none";
+  public static final String REVERSE_FLAG_CANCEL = "cancel";
+  public static final String REVERSE_FLAG_REVERSE = "reverse";
 }
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/ThirdPayCall.kt b/src/main/kotlin/com/supwisdom/dlpay/api/ThirdPayCall.kt
index 4f06db8..3a03934 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/ThirdPayCall.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/ThirdPayCall.kt
@@ -237,6 +237,7 @@
 
         fun CallCitizenCardPay(config: Map<String, String?>, paydtl: TShopdtl): CallBackResp {
             val resp = CallBackResp()
+            resp.retcode = "0"
             // TODO: 代扣逻辑
             return resp
         }
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/advices.kt b/src/main/kotlin/com/supwisdom/dlpay/api/advices.kt
index 47eb043..f96ba95 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/advices.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/advices.kt
@@ -1,10 +1,26 @@
 package com.supwisdom.dlpay.api
 
+import com.supwisdom.dlpay.framework.ResponseBodyBuilder
+import com.supwisdom.dlpay.framework.util.TradeErrorCode
 import org.aspectj.lang.JoinPoint
 import org.aspectj.lang.annotation.Aspect
 import org.aspectj.lang.annotation.Before
 import org.aspectj.lang.annotation.Pointcut
+import org.springframework.http.ResponseEntity
 import org.springframework.stereotype.Component
+import org.springframework.web.bind.annotation.ExceptionHandler
+import org.springframework.web.bind.annotation.RestControllerAdvice
+
+
+@RestControllerAdvice
+class RestControllerAdvice {
+
+    @ExceptionHandler
+    fun handleException(ex: Exception): ResponseEntity<Any> {
+        return ResponseEntity.ok().body(ResponseBodyBuilder.create()
+                .exception(TradeErrorCode.BUSINESS_DEAL_ERROR, ex.cause))
+    }
+}
 
 @Component
 @Aspect
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 0d8007a..bfa6eac 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
@@ -217,15 +217,19 @@
                     transTime = param.transtime
                     paytype = TradeDict.PAYTYPE_CITIZEN_CARD
                     payinfo = "市民卡代扣消费"
+                    outId = shopacc.shopaccno
+                    outtradeno = param.billno
                 }.person(account).apply {
                     amount = param.amount / 100.0
                     opposite = AccountProxy(shopacc)
-                    tradeflag = TradeDict.TRADE_FLAG_PAY
+                    description = "市民卡代扣消费"
                     summary = "市民卡代扣消费"
+                    tradeOut()
                 }.and().shop(shopacc).apply {
                     amount = param.amount / 100.0
                     opposite = AccountProxy(account)
                     summary = "市民卡代扣消费"
+                    tradeIn()
                 }.and().addDebitCreditRecord(AccountProxy(subject), AccountProxy(account),
                         param.amount / 100.0, "市民卡代扣消费")
                         .addDebitCreditRecord(AccountProxy(account), AccountProxy(shopacc),
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
index 5c557d3..21d0324 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/repositories/repository_impl.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/repositories/repository_impl.kt
@@ -62,7 +62,7 @@
                 ?: throw TransactionProcessException(TradeErrorCode.ACCOUNT_NOT_EXISTS,
                         "交易流水<${dtl.refno}>商户账户不存在")
 
-        val query = entityManager.createQuery("update TShopacc c set c.balance=c.balance-?1" +
+        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)
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 f8ece69..4e355cf 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
@@ -5,52 +5,28 @@
 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.TransactionCheckException
 import com.supwisdom.dlpay.exception.TransactionProcessException
 import com.supwisdom.dlpay.framework.service.SystemUtilService
-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.stereotype.Service
 import java.sql.SQLException
-import javax.transaction.Transactional
+import java.sql.Timestamp
 
 
-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 {
 
-    companion object {
-        val INIT_DTL_STATUS = setOf(TradeDict.DTL_STATUS_INIT, TradeDict.DTL_STATUS_WIP)
-    }
-
     @Autowired
     lateinit var transactionMainDao: TransactionMainDao
 
     @Autowired
-    lateinit var accoutDao: AccountDao
-
-    @Autowired
     lateinit var persondtlDao: PersondtlDao
 
     @Autowired
-    lateinit var shopdtlDao: ShopdtlDao
-
-    @Autowired
-    lateinit var subjectdtlDao: SubjectdtlDao
-
-    @Autowired
-    lateinit var debitCreditDtlDao: DebitCreditDtlDao
-
-    @Autowired
     lateinit var accountRepository: TAccountRepositoryImpl
 
     @Autowired
@@ -59,12 +35,91 @@
     @Autowired
     lateinit var systemUtilService: SystemUtilService
 
+    private fun preCheck(builder: TransactionBuilder) {
+        if (builder.outId.isNotEmpty() && builder.outtradeno.isEmpty()) {
+            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                    "第三方账号未指定第三方流水号")
+        }
+
+        if (builder.transDate.length != 8 || builder.transTime.length != 6) {
+            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                    "交易发生日期错误")
+        }
+
+        if (builder.transCode <= 0) {
+            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                    "交易码错误")
+        }
+
+        if (builder.hasPerson()) {
+            builder.person().also {
+                if (!it.hasOpposite()) {
+                    throw  TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                            "个人交易对方账户未设置")
+                }
+                if (it.payinfo.isEmpty() || it.paytype.isEmpty()) {
+                    throw  TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                            "个人交易信息未设置")
+                }
+                when (it.tradeFlag()) {
+                    TradeDict.TRADE_FLAG_IN -> {
+                        throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                                "不支持的交易")
+                    }
+                    TradeDict.TRADE_FLAG_OUT -> {
+                        if (builder.reverseFlag != TradeDict.REVERSE_FLAG_NONE && it.amount > 0) {
+                            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                                    "消费冲正交易,余额不能大于0")
+                        } else if (builder.reverseFlag == TradeDict.REVERSE_FLAG_NONE && it.amount < 0) {
+                            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                                    "消费交易,余额不能小于0")
+                        }
+                    }
+                    else -> {
+                        throw  TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                                "个人交易方向未设置")
+                    }
+                }
+
+            }
+        }
+
+        if (builder.hasShop()) {
+            builder.shop().also {
+                if (!it.hasOpposite()) {
+                    throw  TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                            "商户交易对方账户未设置")
+                }
+                when (it.tradeFlag()) {
+                    TradeDict.TRADE_FLAG_IN -> {
+                        if (it.amount < 0) {
+                            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                                    "商户收入交易金额不能小于0")
+                        }
+                    }
+                    TradeDict.TRADE_FLAG_OUT -> {
+                        if (it.amount > 0) {
+                            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                                    "商户支出交易金额不能小于0")
+                        }
+                    }
+                    else -> throw  TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR,
+                            "商户交易方向未设置")
+                }
+            }
+        }
+    }
+
     private fun builderRecords(builder: TransactionBuilder, status: String): TTransactionMain {
-        // 记录三方的交易流水(个人,商户,科目)
         try {
+            preCheck(builder)
+            // 记录三方的交易流水(个人,商户,科目)
             val transaction = TTransactionMain().apply {
                 refno = systemUtilService.refno
                 accdate = systemUtilService.accdate
+                outTradeNo = builder.outtradeno
+                outId = builder.outId
+                createTime = Timestamp(systemUtilService.sysdatetime.sysdate.time)
             }
 
 
@@ -73,17 +128,25 @@
                     this.refno = transaction.refno
                     this.accdate = transaction.accdate
                     userid = builder.person().person.userid
+                    accountNo = builder.person().person.accno
                     userName = builder.person().person.accname
+                    outtradeno = builder.outtradeno
                     transdate = builder.transDate
                     transtime = builder.transTime
                     befbal = builder.person().person.availbal
-                    amount = builder.person().amount
+                    amount = if (builder.person().tradeFlag() == TradeDict.TRADE_FLAG_IN) {
+                        builder.person().amount
+                    } else {
+                        -builder.person().amount
+                    }
                     paytype = builder.person().paytype
                     payinfo = builder.person().payinfo
                     transcode = builder.transCode
-                    tradeflag = builder.person().tradeflag
+                    tradeflag = builder.person().tradeFlag()
+                    transdesc = builder.person().description
                     oppositeAccNo = builder.person().opposite.getAccountNo()
                     oppositeAccName = builder.person().opposite.getAccountName()
+                    reverseFlag = builder.reverseFlag
                     this.status = status
                 }.also {
                     // save persondtl
@@ -102,6 +165,12 @@
                     this.transDate = builder.transDate
                     this.transTime = builder.transTime
                     this.tradeCode = builder.transCode
+                    amount = if (builder.shop().tradeFlag() == TradeDict.TRADE_FLAG_IN) {
+                        builder.shop().amount
+                    } else {
+                        -builder.shop().amount
+                    }
+                    this.tradeflag = builder.shop().tradeFlag()
                     this.shopaccno = builder.shop().shopacc.shopaccno
                     this.shopname = builder.shop().shopacc.shopname
                     this.oppositeAccNo = builder.shop().opposite.getAccountNo()
@@ -151,8 +220,9 @@
 
 
             if (builder.hasPerson()) {
+                val dc = getDebitOrCredit(builder.person().tradeFlag())
                 sumBalanceFromDetails(transaction.details, builder.person().person.accno,
-                        builder.person().person.subjno, "debit").also {
+                        builder.person().person.subjno, 2, dc).also {
                     if (transaction.personDtl.amount != it) {
                         throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR,
                                 "输入金额错误,个人余额不符<${transaction.personDtl.amount}>")
@@ -161,8 +231,9 @@
             }
 
             if (builder.hasShop()) {
+                val dc = getDebitOrCredit(builder.shop().tradeFlag())
                 sumBalanceFromDetails(transaction.details, builder.shop().shopacc.shopaccno,
-                        builder.shop().shopacc.subjno, "both").also {
+                        builder.shop().shopacc.subjno, 2, dc).also {
                     if (transaction.shopDtl.amount != it) {
                         throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR,
                                 "输入金额错误,商户余额不符<${transaction.shopDtl.amount}>")
@@ -172,7 +243,9 @@
 
             if (builder.hasSubject()) {
                 sumBalanceFromDetails(transaction.details, builder.subject().subject.subjno,
-                        transaction.subjectDtl.subjectno, "both").also {
+                        transaction.subjectDtl.subjectno,
+                        builder.subject().subject.balflag,
+                        "both").also {
                     if (transaction.subjectDtl.amount != it) {
                         throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR,
                                 "输入金额错误,科目余额不符<${transaction.subjectDtl.amount}>")
@@ -180,6 +253,7 @@
                 }
             }
 
+            transaction.status = status
             transactionMainDao.save(transaction)
             return transaction
         } catch (ex: SQLException) {
@@ -187,6 +261,14 @@
         }
     }
 
+    private fun getDebitOrCredit(tradeFlag: String): String {
+        return when (tradeFlag) {
+            TradeDict.TRADE_FLAG_IN -> "credit"
+            TradeDict.TRADE_FLAG_OUT -> "debit"
+            else -> "both"
+        }
+    }
+
     override fun init(builder: TransactionBuilder): TTransactionMain {
         return builderRecords(builder, TradeDict.DTL_STATUS_INIT)
     }
@@ -197,6 +279,7 @@
 
     private fun sumBalanceFromDetails(details: List<TDebitCreditDtl>,
                                       accno: String, subjno: String,
+                                      balanceFlag: Int,
                                       debitOrCredit: String = "debit"): Double {
 
         val debitAmount = details.sumByDouble { line ->
@@ -214,57 +297,64 @@
             }
         }
         return when (debitOrCredit) {
-            "debit" -> debitAmount
-            "credit" -> creditAmount
+            "debit" -> {
+                if (balanceFlag == 2) debitAmount * -1.0 else debitAmount
+            }
+            "credit" -> {
+                if (balanceFlag == 2) creditAmount else creditAmount * -1.0
+            }
             "both" -> creditAmount - debitAmount
             else -> throw TransactionProcessException(TradeErrorCode.BUSINESS_DEAL_ERROR,
                     "结算借贷方向余额错误")
         }
     }
 
-    override fun wip(refno: String): TTransactionMain {
-        val transaction = transactionMainDao.findById(refno).let {
-            if (it.isPresent) it.get() else null
-        } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>参考号错误")
-
-        if (transaction.status != TradeDict.DTL_STATUS_INIT) {
-            throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>状态错误")
-        }
+    private fun updateRecordStatus(transaction: TTransactionMain, status: String) {
         if (transaction.person) {
-            persondtlDao.findById(refno).let {
-                if (!it.isPresent) it.get() else null
-            }?.also {
-                if (it.status != TradeDict.DTL_STATUS_INIT) {
-                    throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "个人流水<$refno>状态错误")
+            transaction.personDtl?.also {
+                if (it.status != transaction.status) {
+                    throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED,
+                            "个人流水<${transaction.refno}>状态错误")
                 }
-                it.status = TradeDict.DTL_STATUS_WIP
-                persondtlDao.save(it)
-            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS, "个人流水<$refno>不存在")
+                it.status = status
+            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
+                    "个人流水<${transaction.refno}>不存在")
         }
 
         if (transaction.shop) {
-            shopdtlDao.findById(refno).let {
-                if (it.isPresent) it.get() else null
-            }?.also {
-                if (it.status != TradeDict.DTL_STATUS_INIT) {
-                    throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "商户流水<$refno>状态错误")
+            transaction.shopDtl?.also {
+                if (it.status != transaction.status) {
+                    throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED,
+                            "商户流水<${transaction.refno}>状态错误")
                 }
-                it.status = TradeDict.DTL_STATUS_WIP
-                shopdtlDao.save(it)
-            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS, "商户流水<$refno>不存在")
+                it.status = status
+            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
+                    "商户流水<${transaction.refno}>不存在")
         }
 
         if (transaction.subject) {
-            subjectdtlDao.findById(refno).let {
-                if (it.isPresent) it.get() else null
-            }?.also {
-                if (it.status != TradeDict.DTL_STATUS_INIT) {
-                    throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "科目流水<$refno>状态错误")
+            transaction.subjectDtl?.also {
+                if (it.status != transaction.status) {
+                    throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED,
+                            "科目流水<${transaction.refno}>状态错误")
                 }
-                it.status = TradeDict.DTL_STATUS_WIP
-                subjectdtlDao.save(it)
-            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS, "科目流水<$refno>不存在")
+                it.status = status
+            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
+                    "科目流水<${transaction.refno}>不存在")
         }
+        transaction.status = status
+    }
+
+    override fun wip(refno: String): TTransactionMain {
+        val transaction = transactionMainDao.findByRefnoForUpdate(refno)
+                ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>参考号错误")
+
+        if (transaction.status != TradeDict.DTL_STATUS_INIT
+                && transaction.status != TradeDict.DTL_STATUS_WIP) {
+            throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>状态错误")
+        }
+
+        updateRecordStatus(transaction, TradeDict.DTL_STATUS_WIP)
         transactionMainDao.save(transaction)
         return transaction
     }
@@ -278,7 +368,8 @@
             throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>状态错误")
         }
 
-        transaction.status = TradeDict.DTL_STATUS_FAIL
+        updateRecordStatus(transaction, TradeDict.DTL_STATUS_FAIL)
+        transaction.endTime = Timestamp(systemUtilService.sysdatetime.sysdate.time)
         transactionMainDao.save(transaction)
         return transaction
     }
@@ -292,25 +383,35 @@
         if (transaction.status in errorStatus) {
             throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>状态错误")
         }
-        transaction.status = TradeDict.DTL_STATUS_FAIL
+        transaction.status = TradeDict.DTL_STATUS_SUCCESS
         if (transaction.person) {
             // update account balance
             transaction.personDtl?.let {
-                accountRepository.recalcAccountBalance(it, it.amount, false)
+                if (accountRepository.recalcAccountBalance(it, it.amount, false) != 1) {
+                    throw TransactionProcessException(TradeErrorCode.ACCOUNT_TRADE_BUSY,
+                            "个人账户交易冲突")
+                }
             } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
                     "个人流水<${transaction.refno}>不存在")
+            transaction.personDtl.status = TradeDict.DTL_STATUS_SUCCESS
         }
         if (transaction.shop) {
             // update shop balance
             transaction.shopDtl?.let {
-                shopaccRepository.recalcShopBalance(it, it.amount, false)
+                if (shopaccRepository.recalcShopBalance(it, it.amount, false) != 1) {
+                    throw TransactionProcessException(TradeErrorCode.ACCOUNT_TRADE_BUSY,
+                            "商户账户交易冲突")
+                }
             } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
                     "商户流水<${transaction.refno}>不存在")
+            transaction.shopDtl.status = TradeDict.DTL_STATUS_SUCCESS
         }
 
         if (transaction.subject) {
             // update subject balance
         }
+
+        transaction.endTime = Timestamp(systemUtilService.sysdatetime.sysdate.time)
         transactionMainDao.save(transaction)
         return transaction
     }
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 847aab8..40abd48 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt
@@ -22,9 +22,34 @@
             return if (field.isEmpty()) parent.payinfo else field
         }
 
+
+    lateinit var opposite: AccountProxy<*>
+
+    fun hasOpposite(): Boolean {
+        return this::opposite.isInitialized
+    }
+
     fun and(): TransactionBuilder {
         return parent
     }
+
+    private var balanceInOut: String = ""
+
+    fun tradeIn(): SubTransactionBuilder {
+        this.balanceInOut = TradeDict.TRADE_FLAG_IN
+        return this
+    }
+
+    fun tradeOut(): SubTransactionBuilder {
+        this.balanceInOut = TradeDict.TRADE_FLAG_OUT
+        return this
+    }
+
+    fun tradeFlag(): String {
+        return this.balanceInOut
+    }
+
+
 }
 
 class PersonTranactionBuilder(parent: TransactionBuilder, val person: TAccount)
@@ -32,8 +57,6 @@
     var amount: Double = 0.0
     var summary: String = ""
     var description: String = ""
-    var tradeflag : String = ""
-    lateinit var opposite: AccountProxy<*>
 
 }
 
@@ -41,14 +64,12 @@
     : SubTransactionBuilder(parent) {
     var amount: Double = 0.0
     var summary: String = ""
-    lateinit var opposite: AccountProxy<*>
 }
 
 class SubjectTransactionBuilder(parent: TransactionBuilder, val subject: TSubject)
     : SubTransactionBuilder(parent) {
     var amount: Double = 0.0
     var summary: String = ""
-    lateinit var opposite: AccountProxy<*>
 
 }
 
@@ -107,6 +128,8 @@
     var paytype: String = ""
     var payinfo: String = ""
     var outtradeno: String = "" //第三方流水号
+    var outId: String = ""
+    var reverseFlag: String = "none"
 
     fun person(): PersonTranactionBuilder {
         return this.personBuilder
@@ -181,73 +204,3 @@
         return transactionService.wip(this)
     }
 }
-
-class TransactionExample {
-    companion object {
-        fun exampleInit(accountUtilServcie: AccountUtilServcie,
-                        service: TransactionService,
-                        userid: String, shopid: Int, amount: Int, manageFee: Int,
-                        transDate: String, transTime: String, payType: String) {
-
-            val person = accountUtilServcie.readAccount(userid)
-            val shop = accountUtilServcie.readShopAcc(shopid)
-
-            val builder = TransactionBuilder().apply {
-                this.transDate = transDate
-                this.transTime = transTime
-                this.transCode = 3001
-                this.payinfo = "POS消费"
-                this.paytype = payType
-            }.person(person).apply {
-                this.amount = (amount + manageFee) / 100.0 // 金额考虑减和加
-                this.summary = "POS消费"
-                this.opposite = AccountProxy(shop)
-                this.tradeflag = TradeDict.TRANS_DIRECTION_CREDIT
-            }.and().shop(shop).apply {
-                this.amount = amount / 100.0 // 金额考虑减和加
-                this.summary = "POS消费"
-                this.opposite = AccountProxy(person)
-            }.and()
-
-            if (payType == "balance") {
-                builder.addDebitCreditRecord(AccountProxy(person), AccountProxy(shop),
-                        amount / 100.0, "POS消费")
-                if (manageFee > 0) {
-                    val subject = accountUtilServcie.readSubject("2001")
-                    builder.subject(subject).apply {
-                        this.amount = manageFee / 100.0 // 金额考虑减和加
-                        this.summary = "POS消费搭伙费"
-                        this.opposite = AccountProxy(person)
-                    }.and().addDebitCreditRecord(AccountProxy(person), AccountProxy(subject), manageFee / 100.0,
-                            "POS消费搭伙费")
-                }
-            } else if (payType == "wechat") {
-                accountUtilServcie.readSubject(Subject.SUBJNO_PAY_WECHAT).also {
-                    builder.addDebitCreditRecord(AccountProxy(it), AccountProxy(person),
-                            (amount + manageFee) / 100.0, "微信转账")
-                            .addDebitCreditRecord(AccountProxy(person), AccountProxy(shop),
-                                    amount / 100.0, "微信支付消费")
-                }
-
-                if (manageFee > 0) {
-                    accountUtilServcie.readSubject("2001").also {
-                        builder.subject(it).apply {
-                            this.amount = manageFee / 100.0 // 金额考虑减和加
-                            this.summary = "微信支付消费搭伙费"
-                            this.opposite = AccountProxy(person)
-                        }.and().addDebitCreditRecord(AccountProxy(person), AccountProxy(it), manageFee / 100.0,
-                                "微信支付消费搭伙费")
-                    }
-                }
-            } else {
-                throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "不支持的支付方式")
-            }
-            val result = builder.init(service)
-            println(result.refno)
-        }
-
-        fun exampleConfirm(refno: String, transactionService: TransactionService) {
-            transactionService.success(refno)
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/framework/framework_util.kt b/src/main/kotlin/com/supwisdom/dlpay/framework/framework_util.kt
index 2137335..b35904b 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/framework/framework_util.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/framework/framework_util.kt
@@ -50,6 +50,12 @@
         return build()
     }
 
+    fun exception(code: Int, cause: Throwable?, msg: String? = null): Map<String, Any> {
+        data("exception", cause?.message ?: "unkonw excpetion")
+        result(code, msg)
+        return build()
+    }
+
     fun transException(exception: TransactionException, msg: String): Map<String, Any> {
         data("exception", exception.message!!)
         result(exception.code(), "$msg - [${exception.message}]")