临时提交
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 806b049..8d3130d 100644
--- a/src/main/java/com/supwisdom/dlpay/api/dao/AccountDao.java
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/AccountDao.java
@@ -1,25 +1,22 @@
 package com.supwisdom.dlpay.api.dao;
 
 import com.supwisdom.dlpay.api.domain.TAccount;
-import com.supwisdom.dlpay.api.domain.TPerson;
+import com.supwisdom.dlpay.api.repositories.TAccountRepository;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Lock;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.jpa.repository.QueryHints;
+import org.springframework.data.jpa.repository.*;
 
 import javax.persistence.LockModeType;
 import javax.persistence.QueryHint;
 
-public interface AccountDao extends JpaRepository<TAccount, String> {
+public interface AccountDao extends JpaRepository<TAccount, String>, TAccountRepository {
 
   @Lock(LockModeType.PESSIMISTIC_WRITE)
   @Query("select a from TAccount a where a.accno = ?1")
   TAccount getByAccnoForUpdate(String accno);
 
   @Lock(LockModeType.PESSIMISTIC_WRITE)
-  @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value ="0")})
+  @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "0")})
   @Query("select a from TAccount a where a.accno = ?1")
   TAccount getByAccnoForUpdateNowait(String accno);
 
@@ -37,5 +34,9 @@
   @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/dao/PersondtlDao.java b/src/main/java/com/supwisdom/dlpay/api/dao/PersondtlDao.java
new file mode 100644
index 0000000..2ee4550
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/PersondtlDao.java
@@ -0,0 +1,7 @@
+package com.supwisdom.dlpay.api.dao;
+
+import com.supwisdom.dlpay.api.domain.TPersondtl;
+import org.springframework.data.repository.CrudRepository;
+
+public interface PersondtlDao extends CrudRepository<TPersondtl, String> {
+}
diff --git a/src/main/java/com/supwisdom/dlpay/api/dao/ShopdtlDao.java b/src/main/java/com/supwisdom/dlpay/api/dao/ShopdtlDao.java
new file mode 100644
index 0000000..42f928f
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/ShopdtlDao.java
@@ -0,0 +1,7 @@
+package com.supwisdom.dlpay.api.dao;
+
+import com.supwisdom.dlpay.api.domain.TShopdtl;
+import org.springframework.data.repository.CrudRepository;
+
+public interface ShopdtlDao extends CrudRepository<TShopdtl, String> {
+}
diff --git a/src/main/java/com/supwisdom/dlpay/api/dao/SubjectdtlDao.java b/src/main/java/com/supwisdom/dlpay/api/dao/SubjectdtlDao.java
new file mode 100644
index 0000000..968a031
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/SubjectdtlDao.java
@@ -0,0 +1,7 @@
+package com.supwisdom.dlpay.api.dao;
+
+import com.supwisdom.dlpay.api.domain.TSubjectdtl;
+import org.springframework.data.repository.CrudRepository;
+
+public interface SubjectdtlDao extends CrudRepository<TSubjectdtl, String> {
+}
diff --git a/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java b/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java
new file mode 100644
index 0000000..649758d
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java
@@ -0,0 +1,18 @@
+package com.supwisdom.dlpay.api.dao;
+
+import com.supwisdom.dlpay.api.domain.TTransactionMain;
+import org.springframework.data.jpa.repository.Lock;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.QueryHints;
+import org.springframework.data.repository.CrudRepository;
+
+import javax.persistence.LockModeType;
+import javax.persistence.QueryHint;
+
+public interface TransactionMainDao extends CrudRepository<TTransactionMain, String> {
+
+  @Lock(LockModeType.PESSIMISTIC_WRITE)
+  @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "0")})
+  @Query(value = "select t from TTransactionMain t where t.refno = ?1")
+  TTransactionMain findByRefnoForUpdate(String refno);
+}
diff --git a/src/main/java/com/supwisdom/dlpay/api/domain/TDebitCreditDtl.java b/src/main/java/com/supwisdom/dlpay/api/domain/TDebitCreditDtl.java
index dc5a9a9..85bac09 100644
--- a/src/main/java/com/supwisdom/dlpay/api/domain/TDebitCreditDtl.java
+++ b/src/main/java/com/supwisdom/dlpay/api/domain/TDebitCreditDtl.java
@@ -12,9 +12,12 @@
   private String refno;
 
   @Id
-  @Column(name = "SEQNO", nullable = false, precision = 1)
+  @Column(name = "SEQNO", nullable = false, precision = 2)
   private Integer seqno;
 
+  @Column(name = "accdate", nullable = false, length = 8)
+  private String accdate;
+
   @Column(name = "DRSUBJNO", length = 10)
   private String drsubjno; //借方科目
 
@@ -97,4 +100,12 @@
   public void setSummary(String summary) {
     this.summary = summary;
   }
+
+  public String getAccdate() {
+    return accdate;
+  }
+
+  public void setAccdate(String accdate) {
+    this.accdate = accdate;
+  }
 }
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 deaeaf3..0e37a32 100644
--- a/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java
+++ b/src/main/java/com/supwisdom/dlpay/api/domain/TPersondtl.java
@@ -7,8 +7,7 @@
     indexes = {@Index(name = "prsndtl_transdate_idx", columnList = "transdate"),
         @Index(name = "prsndtl_accdate_idx", columnList = "accdate"),
         @Index(name = "prsndtl_status_idx", columnList = "status"),
-        @Index(name = "prsndtl_reverse_idx", columnList = "REVERSE_FLAG"),
-        @Index(name = "prsndtl_outtradeno_uk", unique = true, columnList = "outtradeno,shopaccno")})
+        @Index(name = "prsndtl_reverse_idx", columnList = "REVERSE_FLAG")})
 public class TPersondtl {
 
   @Id
@@ -21,6 +20,12 @@
   @Column(name = "USERID", length = 32)
   private String userid;  //用户ID,或账号
 
+  @Column(name = "acccno", length = 32)
+  private String accountNo;
+
+  @Column(name = "USERNAME", length = 200)
+  private String userName;
+
   @Column(name = "TRANSDATE", length = 8)
   private String transdate;
 
@@ -54,10 +59,13 @@
   @Column(name = "OPPOSITEACCNO", length = 20)
   private String oppositeAccNo;
 
+  @Column(name = "OPPOSITEACCNAME", length = 200)
+  private String oppositeAccName;
+
   @Column(name = "OPERID", precision = 9)
   private Integer operid; //操作员ID
 
-  @Column(name = "REVERSE_FLAG", nullable = false, precision = 1)
+  @Column(name = "REVERSE_FLAG", nullable = false, length = 10)
   private String reverseFlag = "none"; //none, cancel, reversed
 
   @Column(name = "REVERSE_AMOUNT", precision = 9, scale = 2)
@@ -242,4 +250,28 @@
   public void setEndtime(String endtime) {
     this.endtime = endtime;
   }
+
+  public String getUserName() {
+    return userName;
+  }
+
+  public void setUserName(String userName) {
+    this.userName = userName;
+  }
+
+  public String getOppositeAccName() {
+    return oppositeAccName;
+  }
+
+  public void setOppositeAccName(String oppositeAccName) {
+    this.oppositeAccName = oppositeAccName;
+  }
+
+  public String getAccountNo() {
+    return accountNo;
+  }
+
+  public void setAccountNo(String accountNo) {
+    this.accountNo = accountNo;
+  }
 }
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 2649d02..ad43a2f 100644
--- a/src/main/java/com/supwisdom/dlpay/api/domain/TShopdtl.java
+++ b/src/main/java/com/supwisdom/dlpay/api/domain/TShopdtl.java
@@ -1,10 +1,12 @@
 package com.supwisdom.dlpay.api.domain;
 
-import javax.persistence.Column;
-import javax.persistence.Id;
-import javax.persistence.Table;
+import javax.persistence.*;
 
-@Table(name = "TB_SHOPDTL")
+@Entity
+@Table(name = "TB_SHOPDTL",
+    indexes = {@Index(name = "shopdtl_accdate", columnList = "accdate"),
+        @Index(name = "shopdtl_shopaccno", columnList = "shopaccno"),
+        @Index(name = "shopdtl_transdate", columnList = "transdate")})
 public class TShopdtl {
   @Id
   @Column(name = "refno", length = 32, nullable = false)
@@ -28,6 +30,9 @@
   @Column(name = "transtime", length = 6)
   private String transTime;
 
+  @Column(name = "tradecode", precision = 8)
+  private Integer tradeCode;
+
   @Column(name = "paytype", length = 20)
   private String payType;
 
@@ -37,6 +42,12 @@
   @Column(name = "status", length = 20)
   private String status;
 
+  @Column(name = "OPPOSITEACCNO", length = 20)
+  private String oppositeAccNo;
+
+  @Column(name = "OPPOSITEACCNAME", length = 200)
+  private String oppositeAccName;
+
   public String getRefno() {
     return refno;
   }
@@ -116,4 +127,28 @@
   public void setStatus(String status) {
     this.status = status;
   }
+
+  public String getOppositeAccNo() {
+    return oppositeAccNo;
+  }
+
+  public void setOppositeAccNo(String oppositeAccNo) {
+    this.oppositeAccNo = oppositeAccNo;
+  }
+
+  public String getOppositeAccName() {
+    return oppositeAccName;
+  }
+
+  public void setOppositeAccName(String oppositeAccName) {
+    this.oppositeAccName = oppositeAccName;
+  }
+
+  public Integer getTradeCode() {
+    return tradeCode;
+  }
+
+  public void setTradeCode(Integer tradeCode) {
+    this.tradeCode = tradeCode;
+  }
 }
diff --git a/src/main/java/com/supwisdom/dlpay/api/domain/TSubjectdtl.java b/src/main/java/com/supwisdom/dlpay/api/domain/TSubjectdtl.java
new file mode 100644
index 0000000..dc3fe04
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/domain/TSubjectdtl.java
@@ -0,0 +1,153 @@
+package com.supwisdom.dlpay.api.domain;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "TB_SUBJECtDTL",
+    indexes = {@Index(name = "subjdtl_accdate", columnList = "accdate"),
+        @Index(name = "subjdtl_subjno", columnList = "subjno")})
+public class TSubjectdtl {
+  @Id
+  @Column(name = "refno", length = 32, nullable = false)
+  private String refno;
+
+  @Column(name = "accdate", length = 8, nullable = false)
+  private String accdate;
+
+  @Column(name = "subjno", length = 10, nullable = false)
+  private String subjectno;
+
+  @Column(name = "subjname", length = 200)
+  private String subjectName;
+
+  @Column(name = "amount", scale = 2, precision = 15)
+  private Double amount;
+
+  @Column(name = "tradecode", precision = 8)
+  private Integer tradeCode;
+
+  @Column(name = "transdate", length = 8)
+  private String transDate;
+
+  @Column(name = "transtime", length = 6)
+  private String transTime;
+
+  @Column(name = "paytype", length = 20)
+  private String payType;
+
+  @Column(name = "payinfo", length = 200)
+  private String payInfo;
+
+  @Column(name = "OPPOSITEACCNO", length = 20)
+  private String oppositeAccNo;
+
+  @Column(name = "OPPOSITEACCNAME", length = 200)
+  private String oppositeAccName;
+
+  @Column(name = "status", length = 20)
+  private String status;
+
+  public String getRefno() {
+    return refno;
+  }
+
+  public void setRefno(String refno) {
+    this.refno = refno;
+  }
+
+  public String getAccdate() {
+    return accdate;
+  }
+
+  public void setAccdate(String accdate) {
+    this.accdate = accdate;
+  }
+
+  public String getSubjectno() {
+    return subjectno;
+  }
+
+  public void setSubjectno(String subjectno) {
+    this.subjectno = subjectno;
+  }
+
+  public String getSubjectName() {
+    return subjectName;
+  }
+
+  public void setSubjectName(String subjectName) {
+    this.subjectName = subjectName;
+  }
+
+  public Double getAmount() {
+    return amount;
+  }
+
+  public void setAmount(Double amount) {
+    this.amount = amount;
+  }
+
+  public String getTransDate() {
+    return transDate;
+  }
+
+  public void setTransDate(String transDate) {
+    this.transDate = transDate;
+  }
+
+  public String getTransTime() {
+    return transTime;
+  }
+
+  public void setTransTime(String transTime) {
+    this.transTime = transTime;
+  }
+
+  public String getPayType() {
+    return payType;
+  }
+
+  public void setPayType(String payType) {
+    this.payType = payType;
+  }
+
+  public String getPayInfo() {
+    return payInfo;
+  }
+
+  public void setPayInfo(String payInfo) {
+    this.payInfo = payInfo;
+  }
+
+  public String getOppositeAccNo() {
+    return oppositeAccNo;
+  }
+
+  public void setOppositeAccNo(String oppositeAccNo) {
+    this.oppositeAccNo = oppositeAccNo;
+  }
+
+  public String getOppositeAccName() {
+    return oppositeAccName;
+  }
+
+  public void setOppositeAccName(String oppositeAccName) {
+    this.oppositeAccName = oppositeAccName;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public Integer getTradeCode() {
+    return tradeCode;
+  }
+
+  public void setTradeCode(Integer tradeCode) {
+    this.tradeCode = tradeCode;
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionMain.java b/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionMain.java
new file mode 100644
index 0000000..dba7948
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionMain.java
@@ -0,0 +1,136 @@
+package com.supwisdom.dlpay.api.domain;
+
+import com.supwisdom.dlpay.framework.domain.TSubject;
+
+import javax.persistence.*;
+
+import static javax.persistence.FetchType.LAZY;
+
+@Entity
+@Table(name = "TB_TRANSACTIONMAIN",
+    indexes = {@Index(name = "transmain_accdate", columnList = "accdate"),
+        @Index(name = "transmain_status", columnList = "status"),
+        @Index(name = "transmain_outtrade", unique = true, columnList = "outid, outtradeno")})
+public class TTransactionMain {
+  @Id
+  @Column(name = "refno", nullable = false, length = 32)
+  private String refno;
+
+  @Column(name = "accdate", nullable = false, length = 8)
+  private String accdate;
+
+  @Column(name = "person")
+  private Boolean person = false;
+
+  @Column(name = "shop")
+  private Boolean shop = false;
+
+  @Column(name = "subject")
+  private Boolean subject = false;
+
+  @Column(name = "status", length = 20)
+  private String status = "none";
+
+  @Column(name = "outtradeno", length = 60)
+  private String outTradeNo = "";
+
+  @Column(name = "outid", length = 60)
+  private String outId;
+
+  @Column(name = "reverse_flag", nullable = false, length = 10)
+  private String reverseFlag = "none";
+
+  @OneToOne(targetEntity = TPersondtl.class, mappedBy = "refno", fetch = LAZY)
+  private TPersondtl personDtl;
+
+  @OneToOne(targetEntity = TShopdtl.class, mappedBy = "refno", fetch = LAZY)
+  private TShopdtl shopDtl;
+
+  @OneToOne(targetEntity = TSubjectdtl.class, mappedBy = "refno", fetch = LAZY)
+  private TSubjectdtl subjectDtl;
+
+  public String getRefno() {
+    return refno;
+  }
+
+  public void setRefno(String refno) {
+    this.refno = refno;
+  }
+
+  public String getAccdate() {
+    return accdate;
+  }
+
+  public void setAccdate(String accdate) {
+    this.accdate = accdate;
+  }
+
+
+  public Boolean getPerson() {
+    return person;
+  }
+
+  public void setPerson(Boolean person) {
+    this.person = person;
+  }
+
+  public Boolean getShop() {
+    return shop;
+  }
+
+  public void setShop(Boolean shop) {
+    this.shop = shop;
+  }
+
+  public Boolean getSubject() {
+    return subject;
+  }
+
+  public void setSubject(Boolean subject) {
+    this.subject = subject;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public String getOutTradeNo() {
+    return outTradeNo;
+  }
+
+  public void setOutTradeNo(String outTradeNo) {
+    this.outTradeNo = outTradeNo;
+  }
+
+  public String getOutId() {
+    return outId;
+  }
+
+  public void setOutId(String outId) {
+    this.outId = outId;
+  }
+
+  public String getReverseFlag() {
+    return reverseFlag;
+  }
+
+  public void setReverseFlag(String reverseFlag) {
+    this.reverseFlag = reverseFlag;
+  }
+
+  public TPersondtl getPersonDtl() {
+    return personDtl;
+  }
+
+  public TShopdtl getShopDtl() {
+    return shopDtl;
+  }
+
+  public TSubjectdtl getSubjectDtl() {
+    return subjectDtl;
+  }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/api/repositories/TAccountRepository.java b/src/main/java/com/supwisdom/dlpay/api/repositories/TAccountRepository.java
new file mode 100644
index 0000000..d999a27
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/repositories/TAccountRepository.java
@@ -0,0 +1,5 @@
+package com.supwisdom.dlpay.api.repositories;
+
+public interface TAccountRepository {
+  int updateAccountBalance(String accno, Double avaibal, Double amount);
+}
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
new file mode 100644
index 0000000..029e85f
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/api/repositories/impl/TAccountRepositoryImpl.java
@@ -0,0 +1,31 @@
+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/java/com/supwisdom/dlpay/framework/util/TradeDict.java b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
index 7bec421..8769f1f 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
@@ -31,6 +31,7 @@
   public static final String DTL_STATUS_FAIL = "fail";
   public static final String DTL_STATUS_CANCEL = "cancel";
   public static final String DTL_STATUS_WIP = "wip";
+  public static final String DTL_STATUS_NONE = "none";
 
   /**
    * 交易借方
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
new file mode 100644
index 0000000..4359f7f
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
@@ -0,0 +1,282 @@
+package com.supwisdom.dlpay.api.service.impl
+
+import com.supwisdom.dlpay.api.TransactionBuilder
+import com.supwisdom.dlpay.api.TransactionResult
+import com.supwisdom.dlpay.api.dao.*
+import com.supwisdom.dlpay.api.domain.*
+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
+
+@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
+
+    private fun builderRecords(builder: TransactionBuilder, status: String): TransactionResult {
+        // 记录三方的交易流水(个人,商户,科目)
+        try {
+            val result = TransactionResult("20000000000001", "20190520")
+            val transaction = TTransactionMain().apply {
+                this.refno = result.refno
+                this.accdate = result.accdate
+            }
+
+            if (builder.hasPerson()) {
+                TPersondtl().apply {
+                    this.refno = result.refno
+                    this.accdate = result.accdate
+                    userid = builder.person().person.userid
+                    userName = builder.person().person.accname
+                    transdate = builder.transDate
+                    transtime = builder.transTime
+                    befbal = builder.person().person.availbal
+                    amount = builder.person().amount
+                    paytype = builder.person().paytype
+                    payinfo = builder.person().payinfo
+                    transcode = builder.transCode
+                    oppositeAccNo = builder.person().opposite.getAccountNo()
+                    oppositeAccName = builder.person().opposite.getAccountName()
+                    this.status = status
+                }.also {
+                    // save persondtl
+                    persondtlDao.save(it)
+                    result.persondtl = it
+                    transaction.person = true
+                }
+            }
+            if (builder.hasShop()) {
+                TShopdtl().apply {
+                    this.refno = result.refno
+                    this.accdate = result.accdate
+                    this.amount = builder.shop().amount
+                    this.payInfo = builder.shop().payinfo
+                    this.payType = builder.shop().paytype
+                    this.transDate = builder.transDate
+                    this.transTime = builder.transTime
+                    this.tradeCode = builder.transCode
+                    this.shopaccno = builder.shop().shopacc.shopaccno
+                    this.shopname = builder.shop().shopacc.shopname
+                    this.oppositeAccNo = builder.shop().opposite.getAccountNo()
+                    this.oppositeAccName = builder.shop().opposite.getAccountName()
+                    this.status = status
+                }.also {
+                    // save shopdtl
+                    shopdtlDao.save(it)
+                    result.shopdtl = it
+                    transaction.shop = true
+                }
+            }
+            if (builder.hasSubject()) {
+                // save subjectdtl
+                TSubjectdtl().apply {
+                    this.refno = result.refno
+                    this.accdate = result.accdate
+                    this.amount = builder.subject().amount
+                    this.payInfo = builder.subject().payinfo
+                    this.payType = builder.subject().paytype
+                    this.tradeCode = builder.transCode
+                    this.transDate = builder.transDate
+                    this.transTime = builder.transTime
+                    this.oppositeAccNo = builder.subject().opposite.getAccountNo()
+                    this.oppositeAccName = builder.subject().opposite.getAccountName()
+                    this.status = status
+                }.also {
+                    subjectdtlDao.save(it)
+                    result.subjectdtl = it
+                    transaction.subject = true
+                }
+            }
+
+            builder.getAllDetails().forEach { line ->
+                TDebitCreditDtl().apply {
+                    this.refno = result.refno
+                    this.accdate = result.accdate
+                    this.seqno = line.seqno
+                    this.draccno = line.debit.getAccountNo()
+                    this.drsubjno = line.debit.getSubjectNo()
+                    this.craccno = line.credit.getAccountNo()
+                    this.crsubjno = line.credit.getSubjectNo()
+                    this.amount = line.amount
+                    this.summary = line.summary
+                }.also {
+                    debitCreditDtlDao.save(it)
+                    result.debitCreditLines.add(it)
+                }
+            }
+
+            val details = debitCreditDtlDao.findByRefno(result.refno)
+
+            if (builder.hasPerson()) {
+                sumBalanceFromDetails(details, builder.person().person.accno,
+                        Subject.SUBJNO_PERSONAL_DEPOSIT).also {
+                    if (result.persondtl.amount != it) {
+                        throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR,
+                                "输入金额错误,个人余额不符<${result.persondtl.amount}>")
+                    }
+                }
+            }
+
+            if (builder.hasShop()) {
+                sumBalanceFromDetails(details, builder.shop().shopacc.shopaccno,
+                        Subject.SUBJNO_MACHANT_INCOME).also {
+                    if (result.shopdtl.amount != it) {
+                        throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR,
+                                "输入金额错误,商户余额不符<${result.shopdtl.amount}>")
+                    }
+                }
+            }
+
+            if (builder.hasSubject()) {
+                sumBalanceFromDetails(details, builder.subject().subject.subjno,
+                        result.subjectdtl.subjectno).also {
+                    if (result.subjectdtl.amount != it) {
+                        throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR,
+                                "输入金额错误,科目余额不符<${result.subjectdtl.amount}>")
+                    }
+                }
+            }
+
+            transactionMainDao.save(transaction)
+            return result
+        } catch (ex: SQLException) {
+            throw TransactionProcessException(TradeErrorCode.BUSINESS_DEAL_ERROR, ex.message)
+        }
+    }
+
+    override fun init(builder: TransactionBuilder): TransactionResult {
+        return builderRecords(builder, TradeDict.DTL_STATUS_INIT)
+    }
+
+    override fun wip(builder: TransactionBuilder): TransactionResult {
+        return builderRecords(builder, TradeDict.DTL_STATUS_WIP)
+    }
+
+    private fun sumBalanceFromDetails(details: List<TDebitCreditDtl>,
+                                      accno: String, subjno: String): Double {
+        return details.sumByDouble { line ->
+            if (line.crsubjno == subjno && line.craccno == accno) {
+                line.amount
+            } else if (line.crsubjno == subjno && line.craccno == accno) {
+                -line.amount
+            } else {
+                0.0
+            }
+        }
+    }
+
+    override fun wip(refno: String) {
+        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>状态错误")
+        }
+        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>状态错误")
+                }
+                it.status = TradeDict.DTL_STATUS_WIP
+                persondtlDao.save(it)
+            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS, "个人流水<$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>状态错误")
+                }
+                it.status = TradeDict.DTL_STATUS_WIP
+                shopdtlDao.save(it)
+            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS, "商户流水<$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>状态错误")
+                }
+                it.status = TradeDict.DTL_STATUS_WIP
+                subjectdtlDao.save(it)
+            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS, "科目流水<$refno>不存在")
+        }
+        transactionMainDao.save(transaction)
+    }
+
+    override fun fail(refno: String) {
+        val transaction = transactionMainDao.findByRefnoForUpdate(refno)
+                ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>参考号错误")
+
+        val errorStatus = setOf(TradeDict.DTL_STATUS_SUCCESS, TradeDict.DTL_STATUS_CANCEL)
+        if (transaction.status in errorStatus) {
+            throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>状态错误")
+        }
+
+        transaction.status = TradeDict.DTL_STATUS_FAIL
+        transactionMainDao.save(transaction)
+    }
+
+    private fun decreseAccountBalance(account: TAccount, amount: Double) {
+
+    }
+
+    override fun success(refno: String) {
+        val transaction = transactionMainDao.findByRefnoForUpdate(refno)
+                ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>参考号错误")
+
+        val errorStatus = setOf(TradeDict.DTL_STATUS_SUCCESS, TradeDict.DTL_STATUS_CANCEL)
+        if (transaction.status in errorStatus) {
+            throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水<$refno>状态错误")
+        }
+        transaction.status = TradeDict.DTL_STATUS_FAIL
+        if (transaction.person) {
+            // update account balance
+            transaction.personDtl?.let {
+                accoutDao.updateAccountBalance("", 0.0, transaction.personDtl.amount)
+            } ?: throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS,
+                    "个人<${transaction.personDtl.userid}>不存在")
+        }
+        if (transaction.shop) {
+            // update shop balance
+        }
+
+        if (transaction.subject) {
+            // update subject balance
+        }
+        transactionMainDao.save(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 c9e940a..b4407c1 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,8 +1,23 @@
 package com.supwisdom.dlpay.api.service
 
 import com.supwisdom.dlpay.api.TransactionBuilder
+import com.supwisdom.dlpay.api.TransactionResult
+import org.springframework.transaction.annotation.Propagation
+import org.springframework.transaction.annotation.Transactional
 
 interface TransactionService {
-//    fun
-    fun transaction(builder: TransactionBuilder)
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+    fun init(builder: TransactionBuilder): TransactionResult
+
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+    fun wip(refno: String)
+
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+    fun wip(builder: TransactionBuilder): TransactionResult
+
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+    fun fail(refno: String)
+
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+    fun success(refno: String)
 }
\ 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 0a7b86f..68c639a 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/transaction_builder.kt
@@ -1,54 +1,51 @@
 package com.supwisdom.dlpay.api
 
-import com.supwisdom.dlpay.api.domain.TAccount
+import com.supwisdom.dlpay.api.domain.*
 import com.supwisdom.dlpay.api.service.AccountUtilServcie
-import com.supwisdom.dlpay.api.service.PersonBalancePayService
+import com.supwisdom.dlpay.api.service.TransactionService
 import com.supwisdom.dlpay.exception.TransactionCheckException
 import com.supwisdom.dlpay.framework.domain.TShopacc
 import com.supwisdom.dlpay.framework.domain.TSubject
 import com.supwisdom.dlpay.framework.util.Subject
 import com.supwisdom.dlpay.framework.util.TradeErrorCode
 
-class PersonTranactionBuilder(val parent: TransactionBuilder) {
-    lateinit var person: TAccount
-    var userid: String = ""
-    var userName: String = ""
-    var amount: Double = 0.0
-    var summary: String = ""
-    var oppositeAccno: String = ""
-    var oppositeName: String = ""
-    var oppositeType: String = ""
+open class SubTransactionBuilder(val parent: TransactionBuilder) {
+    var paytype: String = ""
+        get() {
+            return if (field.isEmpty()) parent.paytype else field
+        }
+
+    var payinfo: String = ""
+        get() {
+            return if (field.isEmpty()) parent.payinfo else field
+        }
 
     fun and(): TransactionBuilder {
         return parent
     }
 }
 
-class ShopTransactionBuilder(val parent: TransactionBuilder) {
-    lateinit var shopacc: TShopacc
-    var shopaccno: String = ""
-    var shopname: String = ""
+class PersonTranactionBuilder(parent: TransactionBuilder, val person: TAccount)
+    : SubTransactionBuilder(parent) {
     var amount: Double = 0.0
     var summary: String = ""
-    var oppositeAccno: String = ""
-    var oppositeName: String = ""
-    var oppositeType: String = ""
+    lateinit var opposite: AccountProxy<*>
 
-    fun and(): TransactionBuilder {
-        return parent
-    }
 }
 
-class SubjectTransactionBuilder(val parent: TransactionBuilder) {
-    lateinit var subject: TSubject
-    var subjno: String = ""
-    var subjName: String = ""
+class ShopTransactionBuilder(parent: TransactionBuilder, val shopacc: TShopacc)
+    : SubTransactionBuilder(parent) {
     var amount: Double = 0.0
     var summary: String = ""
+    lateinit var opposite: AccountProxy<*>
+}
 
-    fun and(): TransactionBuilder {
-        return parent
-    }
+class SubjectTransactionBuilder(parent: TransactionBuilder, val subject: TSubject)
+    : SubTransactionBuilder(parent) {
+    var amount: Double = 0.0
+    var summary: String = ""
+    lateinit var opposite: AccountProxy<*>
+
 }
 
 class AccountProxy<T>(private val accountObj: T) {
@@ -81,6 +78,10 @@
                     "不支持的账户类型, $accountObj")
         }
     }
+
+    fun get(): T {
+        return accountObj
+    }
 }
 
 data class DebitCreditLine(val debit: AccountProxy<*>, val credit: AccountProxy<*>,
@@ -99,26 +100,64 @@
     var transTime: String = ""
     var transCode: Int = 0 //交易码,各明细流水统一
     var refno: String = ""
+    var paytype: String = ""
+    var payinfo: String = ""
     var outtradeno: String = "" //第三方流水号
     val extendMap = mutableMapOf<String, String>() //存调第三方需要的参数信息,存数据库 TB_USERDTL_BUSINESS
     val resultMap = mutableMapOf<String, String>() //存调第三方结果数据
 
     fun person(): PersonTranactionBuilder {
-        return PersonTranactionBuilder(this).also {
-            this.personBuilder = it
+        return this.personBuilder
+    }
+
+    fun person(account: TAccount): PersonTranactionBuilder {
+        return if (!this::personBuilder.isInitialized) {
+            PersonTranactionBuilder(this, account).also {
+                this.personBuilder = it
+            }
+        } else {
+            this.personBuilder
         }
     }
 
+    fun hasPerson(): Boolean {
+        return this::personBuilder.isInitialized
+    }
+
     fun shop(): ShopTransactionBuilder {
-        return ShopTransactionBuilder(this).also {
-            this.shopBuilder = it
+        return this.shopBuilder
+    }
+
+    fun shop(shopacc: TShopacc): ShopTransactionBuilder {
+        return if (!this::shopBuilder.isInitialized) {
+            ShopTransactionBuilder(this, shopacc).also {
+                this.shopBuilder = it
+            }
+        } else {
+            this.shopBuilder
+        }
+    }
+
+    fun hasShop(): Boolean {
+        return this::shopBuilder.isInitialized
+    }
+
+    fun subject(acc: TSubject): SubjectTransactionBuilder {
+        return if (!this::subjectBuilder.isInitialized) {
+            SubjectTransactionBuilder(this, acc).also {
+                this.subjectBuilder = it
+            }
+        } else {
+            this.subjectBuilder
         }
     }
 
     fun subject(): SubjectTransactionBuilder {
-        return SubjectTransactionBuilder(this).also {
-            this.subjectBuilder = it
-        }
+        return this.subjectBuilder
+    }
+
+    fun hasSubject(): Boolean {
+        return this::subjectBuilder.isInitialized
     }
 
     fun <T, U> addDebitCreditRecord(debit: AccountProxy<T>, credit: AccountProxy<U>,
@@ -128,17 +167,36 @@
         return this
     }
 
-    fun init(transactionService: PersonBalancePayService) {
-
+    fun getAllDetails(): List<DebitCreditLine> {
+        return debitCreditLines.toList()
     }
+
+    fun init(transactionService: TransactionService): TransactionResult {
+        return transactionService.init(this)
+    }
+
+    fun wip(transactionService: TransactionService): TransactionResult {
+        return transactionService.wip(this)
+    }
+}
+
+class TransactionResult(val refno: String, val accdate: String) {
+
+    lateinit var persondtl: TPersondtl
+
+    lateinit var shopdtl: TShopdtl
+
+    lateinit var subjectdtl: TSubjectdtl
+
+    var debitCreditLines = mutableListOf<TDebitCreditDtl>()
 }
 
 class TransactionExample {
     companion object {
-        fun example(accountUtilServcie: AccountUtilServcie,
-                    service: PersonBalancePayService,
-                    userid: String, shopid: Int, amount: Int, manageFee: Int,
-                    transDate: String, transTime: String, payType: String) {
+        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)
@@ -147,14 +205,16 @@
                 this.transDate = transDate
                 this.transTime = transTime
                 this.transCode = 3001
-            }.person().apply {
-                this.person = person
+                this.payinfo = "POS消费"
+                this.paytype = payType
+            }.person(person).apply {
                 this.amount = (amount + manageFee) / 100.0 // 金额考虑减和加
                 this.summary = "POS消费"
-            }.and().shop().apply {
-                this.shopacc = shop
+                this.opposite = AccountProxy(shop)
+            }.and().shop(shop).apply {
                 this.amount = amount / 100.0 // 金额考虑减和加
                 this.summary = "POS消费"
+                this.opposite = AccountProxy(person)
             }.and()
 
             if (payType == "balance") {
@@ -162,10 +222,10 @@
                         amount / 100.0, "POS消费")
                 if (manageFee > 0) {
                     val subject = accountUtilServcie.readSubject("2001")
-                    builder.subject().apply {
-                        this.subject = subject
+                    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消费搭伙费")
                 }
@@ -179,10 +239,10 @@
 
                 if (manageFee > 0) {
                     accountUtilServcie.readSubject("2001").also {
-                        builder.subject().apply {
-                            this.subject = it
+                        builder.subject(it).apply {
                             this.amount = manageFee / 100.0 // 金额考虑减和加
                             this.summary = "微信支付消费搭伙费"
+                            this.opposite = AccountProxy(person)
                         }.and().addDebitCreditRecord(AccountProxy(person), AccountProxy(it), manageFee / 100.0,
                                 "微信支付消费搭伙费")
                     }
@@ -190,7 +250,12 @@
             } else {
                 throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "不支持的支付方式")
             }
-            builder.init(service)
+            val result = builder.init(service)
+            println(result.refno)
+        }
+
+        fun exampleConfirm(refno: String, transactionService: TransactionService) {
+            transactionService.success(refno)
         }
     }
 }
\ No newline at end of file