消费初步实现
diff --git a/src/main/java/com/supwisdom/dlpay/consume/dao/AccountDao.java b/src/main/java/com/supwisdom/dlpay/consume/dao/AccountDao.java
index e82cb11..b2a7892 100644
--- a/src/main/java/com/supwisdom/dlpay/consume/dao/AccountDao.java
+++ b/src/main/java/com/supwisdom/dlpay/consume/dao/AccountDao.java
@@ -11,8 +11,9 @@
 public interface AccountDao extends JpaRepository<TAccount, String> {
 
   @Lock(LockModeType.PESSIMISTIC_WRITE)
-  //@Query("select a from taccount a where a.accno = ?1")
-  TAccount getByAccno(String accno);
+  @Query("select a from TAccount a where a.accno = ?1")
+  TAccount getByAccnoForUpdate(String accno);
+
 
   TAccount findByUserid(String userid);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/consume/dao/DebitCreditDtlDao.java b/src/main/java/com/supwisdom/dlpay/consume/dao/DebitCreditDtlDao.java
index d841c70..220ff83 100644
--- a/src/main/java/com/supwisdom/dlpay/consume/dao/DebitCreditDtlDao.java
+++ b/src/main/java/com/supwisdom/dlpay/consume/dao/DebitCreditDtlDao.java
@@ -18,4 +18,7 @@
       "where b.status='success' and b.accdate=:settledate " +
       "group by a.drsubjno,a.draccno,a.crsubjno,a.craccno,a.summary ", nativeQuery = true)
   List<VoucherTemp> getVoucherData(@RequestParam("settledate") String settledate);
+
+
+  List<TDebitCreditDtl> findByRefno(String refno);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/consume/domain/TAccount.java b/src/main/java/com/supwisdom/dlpay/consume/domain/TAccount.java
index cdb9cab..9dd87c1 100644
--- a/src/main/java/com/supwisdom/dlpay/consume/domain/TAccount.java
+++ b/src/main/java/com/supwisdom/dlpay/consume/domain/TAccount.java
@@ -1,5 +1,7 @@
 package com.supwisdom.dlpay.consume.domain;
 
+import com.supwisdom.dlpay.framework.util.MD5;
+import com.supwisdom.dlpay.framework.util.MoneyUtil;
 import org.hibernate.annotations.GenericGenerator;
 
 import javax.persistence.*;
@@ -230,4 +232,46 @@
   public void setClosedate(String closedate) {
     this.closedate = closedate;
   }
+
+  public String  generateTac(){
+    String data = this.accno+ MoneyUtil.YuanToFen(this.availbal)+MoneyUtil.YuanToFen(this.balance)+MoneyUtil.YuanToFen(this.frozebal);
+    return MD5.generatePassword(data, this.accno);
+  }
+
+  public boolean tacCheck(){
+    String tac_c = generateTac();
+    if (tac_c.equalsIgnoreCase(this.tac)||this.tac==null) {
+      return true;
+    }
+    return false;
+  }
+
+  public void addAmount(double amount) {
+    this.balance = this.balance + amount;
+    this.availbal = this.availbal + amount;
+    this.tac = this.generateTac();
+  }
+
+  public boolean checkOverflow(){
+    if(null!=this.maxbal && this.maxbal>0){
+      if(MoneyUtil.moneyCompare(this.balance,this.maxbal)>0){
+        return true; //超出账户最大值
+      }
+    }
+    return false;
+  }
+
+  public boolean checkOverdraft() {
+    if (MoneyUtil.moneyCompare(this.balance, 0) < 0) {
+      return true; //欠费透支
+    }
+    return false;
+  }
+
+  public boolean checkOverdraft(double limit) {
+    if (MoneyUtil.moneyCompare(this.balance, limit) < 0) {
+      return true; //余额低于某阀值
+    }
+    return false;
+  }
 }
diff --git a/src/main/java/com/supwisdom/dlpay/consume/domain/TPerson.java b/src/main/java/com/supwisdom/dlpay/consume/domain/TPerson.java
index 8a2cb71..c5bceb6 100644
--- a/src/main/java/com/supwisdom/dlpay/consume/domain/TPerson.java
+++ b/src/main/java/com/supwisdom/dlpay/consume/domain/TPerson.java
@@ -61,10 +61,7 @@
   public TPerson() {
   }
 
-  public TPerson(String userid, String name, String sex, String idtype, String idno, String country, String nation,
-                 String email, String tel, String mobile, String addr, String zipcode, String lastsaved,
-                 String thirdUniqueIdenty) {
-    this.userid = userid;
+  public TPerson(String name, String sex, String status, String idtype, String idno, String country, String nation, String email, String tel, String mobile, String addr, String zipcode, String lastsaved, String thirdUniqueIdenty) {
     this.name = name;
     this.sex = sex;
     this.status = status;
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/MD5.java b/src/main/java/com/supwisdom/dlpay/framework/util/MD5.java
new file mode 100644
index 0000000..4d5ba84
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/MD5.java
@@ -0,0 +1,70 @@
+package com.supwisdom.dlpay.framework.util;
+
+import java.security.MessageDigest;
+
+public class MD5 {
+	//十六进制下数字到字符的映射数组  
+    private final static String[] hexDigits = {"0", "1", "2", "3", "4",
+        "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};  
+      
+    /** * 把inputString加密     */  
+    public static String generatePassword(String data, String salt){
+    	String enyptString = data+"{"+salt+"}";
+        return encodeByMD5(enyptString);  
+    }  
+      
+      /** 
+       * 验证输入的密码是否正确 
+     * @param password    加密后的密码 
+     * @param inputString    输入的字符串 
+     * @return    验证结果,TRUE:正确 FALSE:错误 
+     */  
+    public static boolean validatePassword(String password, String inputString){
+        if(password.equals(encodeByMD5(inputString))){  
+            return true;  
+        } else{  
+            return false;  
+        }  
+    }  
+    /**  对字符串进行MD5加密     */
+    public static String encodeByMD5(String originString){
+        if (originString != null){  
+            try{  
+                //创建具有指定算法名称的信息摘要  
+                MessageDigest md = MessageDigest.getInstance("MD5");
+                //使用指定的字节数组对摘要进行最后更新,然后完成摘要计算  
+                byte[] results = md.digest(originString.getBytes("UTF-8"));
+                //将得到的字节数组变成字符串返回  
+                String resultString = byteArrayToHexString(results);
+                return resultString.toUpperCase();  
+            } catch(Exception ex){
+                ex.printStackTrace();  
+            }  
+        }  
+        return null;  
+    }  
+      
+    /**  
+     * 转换字节数组为十六进制字符串 
+     * @param     字节数组 
+     * @return    十六进制字符串 
+     */  
+    private static String byteArrayToHexString(byte[] b){
+        StringBuffer resultSb = new StringBuffer();
+        for (int i = 0; i < b.length; i++){  
+            resultSb.append(byteToHexString(b[i]));  
+        }  
+        return resultSb.toString();  
+    }  
+      
+    /** 将一个字节转化成十六进制形式的字符串     */  
+    private static String byteToHexString(byte b){
+        int n = b;  
+        if (n < 0)  
+            n = 256 + n;  
+        int d1 = n / 16;  
+        int d2 = n % 16;  
+        return hexDigits[d1] + hexDigits[d2];  
+    }
+
+}
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 6c89ce0..1c4e046 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/TradeErrorCode.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/TradeErrorCode.java
@@ -49,5 +49,29 @@
    * */
   public static final int SUBJECT_NOT_EXISTS = 10008;
 
+  /**
+   * 外部流水号重复
+   * */
+  public static final int OUTTRADENO_ALREADY_EXISTS = 10009;
+
+  /**
+   * 账户TAC校验异常
+   * */
+  public static final int ACCOUNT_TAC_ERROR = 10010;
+
+  /**
+   * 账户余额超上限
+   * */
+  public static final int OVERFLOW_BALANCE_ERROR = 10011;
+
+  /**
+   * 未指定明确的交易结束状态
+   * */
+  public static final int TRANSDTL_STATUS_ERROR = 10012;
+
+  /**
+   * 非初始化流水
+   * */
+  public static final int TRANSDTL_STATUS_NOT_INIT = 10013;
 
 }
diff --git a/src/main/java/com/supwisdom/dlpay/util/MoneyUtil.java b/src/main/java/com/supwisdom/dlpay/util/MoneyUtil.java
index 2fb8b73..fac8ed4 100644
--- a/src/main/java/com/supwisdom/dlpay/util/MoneyUtil.java
+++ b/src/main/java/com/supwisdom/dlpay/util/MoneyUtil.java
@@ -5,24 +5,26 @@
 

 public class MoneyUtil {

 

-	public static int YuanToFen(double yuan) {

-		return (int) (Math.round(yuan * 100));

-	}

+  public static int YuanToFen(double yuan) {

+    return (int) (Math.round(yuan * 100));

+  }

 

-	public static boolean moneyCompare(double x1, double x2) {

-		return YuanToFen(x1) == YuanToFen(x2);

-	}

-	public static double formatYuan(double yuan) {

-		DecimalFormat df = new DecimalFormat("##0.00");

-		double money = ((double) YuanToFen(yuan)) / 100;

-		return Double.valueOf(df.format(money));

-	}

+  public static boolean moneyCompare(double x1, double x2) {

+    return YuanToFen(x1) == YuanToFen(x2);

+  }

 

-	public static double FenToYuan(int fen) {

-		return formatYuan(fen / 100.0);

-	}

-	public static String format(double num,String format) {

-		DecimalFormat df = new DecimalFormat(format);

-		return df.format(num);

-	}

+  public static double formatYuan(double yuan) {

+    DecimalFormat df = new DecimalFormat("##0.00");

+    double money = ((double) YuanToFen(yuan)) / 100;

+    return Double.valueOf(df.format(money));

+  }

+

+  public static double FenToYuan(int fen) {

+    return formatYuan(fen / 100.0);

+  }

+

+  public static String format(double num, String format) {

+    DecimalFormat df = new DecimalFormat(format);

+    return df.format(num);

+  }

 }

diff --git a/src/main/kotlin/com/supwisdom/dlpay/consume/comsume_builder.kt b/src/main/kotlin/com/supwisdom/dlpay/consume/comsume_builder.kt
index 8243a78..67257c6 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/consume/comsume_builder.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/consume/comsume_builder.kt
@@ -9,10 +9,7 @@
 import com.supwisdom.dlpay.framework.domain.TShopacc
 import com.supwisdom.dlpay.framework.domain.TSubject
 import com.supwisdom.dlpay.framework.domain.TTranstype
-import com.supwisdom.dlpay.framework.util.TradeDict
-import com.supwisdom.dlpay.framework.util.TradeErrorCode
-import com.supwisdom.dlpay.framework.util.Tradetype
-import org.springframework.beans.factory.annotation.Autowired
+import com.supwisdom.dlpay.framework.util.*
 
 class AccountHolder<T> private constructor(val accountId: String, val idType: Int) {
     companion object {
@@ -70,6 +67,8 @@
     lateinit var person: TPerson
     lateinit var tradetype:Tradetype
 
+    var transcode = 0
+
     var transDate = ""
     var transTime = ""
     /**
@@ -78,22 +77,23 @@
     var overdraft = false
     var description = ""
 
-
     // 内部参数,不需要调用者处理
     val details = mutableListOf<TransDetail>()
     var amount: Double = 0.0
     var accountUtil = accUitl
 
+
     /**
      * 支付方式
      * */
-    var paytype = ""
+    var paytype = ""  //枚举?
     var payinfo = ""
 
     /**
      * 外部流水号
      * */
-    var outtradeno = ""
+    var outtradeno = "" //发起支付系统的流水号(对接系统根据此流水号查询本地流水对账)
+
 
     fun setOwner(per: TPerson): PersonTransBuilder {
         this.person = per
@@ -111,6 +111,12 @@
         return this
     }
 
+    fun setTransinfo(transcode: Int, description: String): PersonTransBuilder {
+        this.transcode = transcode
+        this.description = description
+        return this
+    }
+
     fun selectPaytype(paytype: String, payinfo: String): PersonTransBuilder {
         this.paytype = paytype
         this.payinfo = payinfo
@@ -122,32 +128,75 @@
         return this
     }
 
-    fun chooseTradetype(tradetype: Tradetype) {
+    fun chooseTradetype(tradetype: Tradetype): PersonTransBuilder {
         this.tradetype = tradetype
+        return this
+    }
+
+    fun setTradeno(outtradeno: String): PersonTransBuilder {
+        this.outtradeno = outtradeno
+        return this
     }
 
     private fun prepareData() {
-        if (null == this.person) {
-            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "未指定交易用户")
+        if (null == this.tradetype) {
+            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "未指定交易类型")
+        }
+        if(transcode==0){
+            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "未指定交易码")
+        }
+
+        when(this.tradetype){
+            //充值必须指明用户和支付方式
+            Tradetype.RECHARGE -> {
+                if (null == this.person)
+                    throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "未指定充值账户")
+
+                if (StringUtil.isEmpty(this.paytype))
+                    throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "未指定充值方式")
+            }
+
+            //消费
+            Tradetype.CONSUME ->{
+                if (StringUtil.isEmpty(this.paytype))
+                    this.paytype = TradeDict.PAYTYPE_BALANCE   //默认余额支付
+            }
         }
 
         amount = 0.0
-        if(this.details.size<1){
+        if (this.details.size < 1) {
             throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "未指定交易明细")
         }
+        if (null != this.person) {
+            var buyer = accountUtil.readAccount(person.userid) //判断是一个人的流水
+            for (detail in details) {
+                if (detail.debitSubjNo.equals(buyer.subjno) && !detail.debitAccNo.equals(buyer.accno))
+                    throw throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "交易明细用户错误")
+
+                if (detail.creditSubjNo.equals(buyer.subjno) && !detail.creditAccNo.equals(buyer.accno))
+                    throw throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "交易明细用户错误")
+            }
+        }
         amount = this.details.sumByDouble { it.amount }
 
+        if (!StringUtil.isEmpty(this.transDate) && !DateUtil.checkDatetimeValid(this.transDate, "yyyyMMdd")) {
+            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "交易日期格式错误[yyyyMMdd]")
+        }
+        if (!StringUtil.isEmpty(this.transTime) && !DateUtil.checkDatetimeValid(this.transTime, "HHmmss")) {
+            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "交易时间格式错误[HHmmss]")
+        }
 
-
-
-
+        if(StringUtil.isEmpty(this.outtradeno)){
+            throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "未传递外部流水号")
+        }
     }
 
     private fun preCheckAccount() {
-
-        when(person.status){
-            TradeDict.STATUS_CLOSED -> throw TransactionCheckException(TradeErrorCode.PERSON_STATUS_ERROR, "用户已注销")
-            TradeDict.STATUS_LOCKED -> throw TransactionCheckException(TradeErrorCode.PERSON_STATUS_ERROR, "用户已冻结锁定")
+        if (null != this.person) {
+            when (person.status) {
+                TradeDict.STATUS_CLOSED -> throw TransactionCheckException(TradeErrorCode.PERSON_STATUS_ERROR, "用户已注销")
+                TradeDict.STATUS_LOCKED -> throw TransactionCheckException(TradeErrorCode.PERSON_STATUS_ERROR, "用户已冻结锁定")
+            }
         }
     }
 
@@ -217,10 +266,10 @@
      * @param status - 完成交易状态,见 TradeDict.DTL_STATUS_FAIL
      */
     fun done(paydtl: TUserdtl, status: String, service: PersonBalancePayService): TUserdtl {
-        return service.finish(paydtl, status, this)
+        return service.finish(paydtl, status, null)
     }
 
     fun done(refno: String, status: String, service: PersonBalancePayService): TUserdtl {
-        return service.finish(refno, status, this)
+        return service.finish(refno, status, null)
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/consume/service/impl/pay_service_impl.kt b/src/main/kotlin/com/supwisdom/dlpay/consume/service/impl/pay_service_impl.kt
index 8ce1108..44c0a56 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/consume/service/impl/pay_service_impl.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/consume/service/impl/pay_service_impl.kt
@@ -2,8 +2,10 @@
 
 import com.supwisdom.dlpay.consume.PersonTransBuilder
 import com.supwisdom.dlpay.consume.dao.AccountDao
+import com.supwisdom.dlpay.consume.dao.DebitCreditDtlDao
 import com.supwisdom.dlpay.consume.dao.UserdtlDao
 import com.supwisdom.dlpay.consume.domain.TAccount
+import com.supwisdom.dlpay.consume.domain.TDebitCreditDtl
 import com.supwisdom.dlpay.consume.domain.TUserdtl
 import com.supwisdom.dlpay.consume.service.AccountUtilServcie
 import com.supwisdom.dlpay.consume.service.PersonBalancePayService
@@ -14,6 +16,7 @@
 import com.supwisdom.dlpay.framework.domain.TSubject
 import com.supwisdom.dlpay.framework.domain.TTranstype
 import com.supwisdom.dlpay.framework.service.SystemUtilService
+import com.supwisdom.dlpay.framework.util.StringUtil
 import com.supwisdom.dlpay.framework.util.TradeDict
 import com.supwisdom.dlpay.framework.util.TradeErrorCode
 import com.supwisdom.dlpay.framework.util.Tradetype
@@ -36,7 +39,7 @@
 
 
     override fun readAccountForUpdate(userid: String, nowait: Boolean): TAccount {
-       //TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+        //TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
         return accountDao.findByUserid(userid);
     }
 
@@ -50,7 +53,7 @@
     }
 
     override fun readShopAccForUpdate(shopId: Int, nowait: Boolean): TShopacc {
-       // TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+        // TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
         return shopaccDao.findByShopid(shopId)
     }
 
@@ -87,6 +90,8 @@
 class PersonBalancePayServiceImpl : PersonBalancePayService {
     @Autowired
     lateinit var userdtlDao: UserdtlDao
+    @Autowired
+    lateinit var debitCreditDtlDao: DebitCreditDtlDao
 
     @Autowired
     lateinit var accountDao: AccountDao
@@ -98,64 +103,173 @@
     lateinit var systemUtilService: SystemUtilService
 
 
-    private fun lockAccount(builder: PersonTransBuilder): TAccount {
-        TODO("")
+    private fun getlockAccount(accno: String): TAccount {
+        //TODO 等待锁
+        return accountDao.getOne(accno)
     }
 
-    override fun process(builder: PersonTransBuilder): TUserdtl {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+    private fun getlockAccountNowait(accno: String): TAccount {
+        //TODO 等待锁
+        return accountDao.getOne(accno)
     }
 
-    override fun init(builder: PersonTransBuilder): TUserdtl {
-
-        val userdtl = TUserdtl()
-        val refno = systemUtilService.refno
-        val systemtime = systemUtilService.sysdatetime
-        userdtl.refno = refno
-        userdtl.amount = builder.amount
-        userdtl.createtime = systemtime.hostdatetime
-        userdtl.status = TradeDict.DTL_STATUS_INIT
-        userdtl.transdate = builder.transDate
-        userdtl.transtime = builder.transTime
-        userdtl.userid = builder.person?.userid
-        userdtl.outtradeno = builder.outtradeno
-        userdtl.payinfo = builder.payinfo
-        userdtl.paytype = builder.paytype
-        userdtl.tradeflag = 2
-        userdtlDao.save(userdtl)
-        return userdtl
-    }
-
-    override fun wip(paydtl: TUserdtl, builder: PersonTransBuilder): TUserdtl {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
-    }
-
-    override fun wip(refno: String, builder: PersonTransBuilder): TUserdtl {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
-    }
-
-    override fun finish(paydtl: TUserdtl, status: String, builder: PersonTransBuilder): TUserdtl {
-        if(paydtl.status==TradeDict.DTL_STATUS_SUCCESS){
-            return paydtl
-        }
-        paydtl.status = status
-        if(status==TradeDict.DTL_STATUS_SUCCESS){
-            //TODO 更新商户余额,科目余额,账户支付的需要更新余额
-        }
-        val systemtime = systemUtilService.sysdatetime
-        paydtl.endtime = systemtime.hostdatetime
-        userdtlDao.save(paydtl)
-        return paydtl
-    }
-
-    override fun finish(refno: String, status: String, builder: PersonTransBuilder): TUserdtl {
-        userdtlDao.findById(refno).let {
-            if (it.isPresent) {
-                return finish(it.get(), status, builder)
+    private fun getLockUserdtlNowait(refno: String): TUserdtl {
+        //TODO 加锁,非等待
+        userdtlDao.findByRefno(refno).let {
+            if (null != it) {
+                return it
             }
             throw TransactionProcessException(TradeErrorCode.TRANSACTION_NOT_EXISTS, "交易参考号<$refno>不存在")
         }
     }
 
+    private fun getLockUserdtl(refno: String): TUserdtl {
+        //TODO 加锁,等待
+        return userdtlDao.findByRefno(refno)
+    }
 
+    private fun checkOuttradenoExist(outtradeno: String): Boolean {
+//        TODO("判断 outtradeno 重复发起")
+        return false
+    }
+
+    private fun doDealAccount(accno: String, amount: Double, overdraft: Boolean): TAccount {
+        var account = getlockAccount(accno)
+        if (account.tacCheck())
+            throw TransactionProcessException(TradeErrorCode.ACCOUNT_TAC_ERROR, "账户<$accno>tac校验异常")
+
+        account.addAmount(amount) //入账
+        if (account.checkOverflow())
+            throw TransactionProcessException(TradeErrorCode.OVERFLOW_BALANCE_ERROR, "账户<$accno>已超最大余额限制")
+
+        if (!overdraft && account.checkOverdraft())
+            throw TransactionProcessException(TradeErrorCode.SHORT_BALANCE_ERROR, "账户<$accno>余额不足")
+
+        return accountDao.save(account) //入库更新
+    }
+
+    private fun doDealShopacc(shopaccno: String, amount: Double) {
+        TODO("商户更新余额逻辑")
+    }
+
+    override fun process(builder: PersonTransBuilder): TUserdtl {
+        return finish(init(builder), TradeDict.DTL_STATUS_SUCCESS, null)
+    }
+
+    override fun init(builder: PersonTransBuilder): TUserdtl {
+        var userdtl = TUserdtl()
+        userdtl.refno = systemUtilService.refno
+        userdtl.accdate = systemUtilService.accdate
+        userdtl.userid = builder.person?.userid
+        if (StringUtil.isEmpty(builder.transDate)) {
+            userdtl.transdate = systemUtilService.sysdatetime.hostdate
+        } else {
+            userdtl.transdate = builder.transDate
+        }
+        if (StringUtil.isEmpty(builder.transTime)) {
+            userdtl.transdate = systemUtilService.sysdatetime.hosttime
+        } else {
+            userdtl.transtime = builder.transTime
+        }
+        userdtl.paytype = builder.paytype
+        userdtl.payinfo = builder.payinfo
+        userdtl.transcode = builder.transcode
+        if (StringUtil.isEmpty(builder.description)) {
+            userdtl.transdesc = systemUtilService.getTranscodeName(builder.transcode, null);
+        } else {
+            userdtl.transdesc = builder.description
+        }
+        userdtl.outtradeno = builder.outtradeno
+//        userdtl.operid =
+        when (builder.tradetype) {
+            Tradetype.RECHARGE -> userdtl.tradeflag = 1
+            Tradetype.CONSUME -> userdtl.tradeflag = 2
+        }
+        userdtl.createtime = systemUtilService.sysdatetime.hostdatetime
+        if (checkOuttradenoExist(userdtl.outtradeno)) {
+            throw TransactionProcessException(TradeErrorCode.OUTTRADENO_ALREADY_EXISTS, "外部流水号重复")
+        }
+
+        userdtl.amount = builder.amount
+        userdtl.status = TradeDict.DTL_STATUS_INIT
+        userdtlDao.save(userdtl)
+
+        for (detail in builder.details) {
+            var dtl = TDebitCreditDtl()
+            dtl.refno = userdtl.refno
+            dtl.seqno = detail.rowno
+            dtl.drsubjno = detail.debitSubjNo
+            dtl.draccno = detail.debitAccNo
+            dtl.crsubjno = detail.creditSubjNo
+            dtl.craccno = detail.creditAccNo
+            dtl.amount = detail.amount
+            dtl.summary = detail.summary
+            debitCreditDtlDao.save(dtl)
+        }
+        return userdtl
+    }
+
+    override fun finish(paydtl: TUserdtl, status: String, businessData: Map<String, String>?): TUserdtl {
+        return finish(paydtl.refno, status, businessData)
+    }
+
+    override fun finish(refno: String, status: String, businessData: Map<String, String>?): TUserdtl {
+        var userdtl = getLockUserdtl(refno)
+        when (status) {
+            TradeDict.DTL_STATUS_FAIL -> {
+                //失败
+                if (TradeDict.DTL_STATUS_SUCCESS == userdtl.status)
+                    throw TransactionProcessException(TradeErrorCode.TRANSACTION_IS_FINISHED, "流水已经交易成功")
+                userdtl.status = TradeDict.DTL_STATUS_FAIL
+                userdtl.endtime = systemUtilService.sysdatetime.hostdatetime
+                userdtl.remark = businessData?.get("errmsg")
+                return userdtlDao.save(userdtl)
+            }
+
+            TradeDict.DTL_STATUS_SUCCESS -> {
+                //成功
+                if (TradeDict.DTL_STATUS_SUCCESS == userdtl.status)
+                    return userdtl //已成功直接返回
+
+                for (detail in debitCreditDtlDao.findByRefno(userdtl.refno)) {
+                    //个人账户入账
+                    if (TradeDict.SUBJNO_ACCOUNT == detail.drsubjno) {
+                        doDealAccount(detail.drsubjno, -1 * detail.amount, false) //借方消费
+                    }
+                    if (TradeDict.SUBJNO_ACCOUNT == detail.crsubjno) {
+                        doDealAccount(detail.drsubjno, detail.amount, false) //贷方充值
+                    }
+
+                    //商户入账
+                    if (TradeDict.SUBJNO_SHOP == detail.drsubjno) {
+                        doDealShopacc(detail.draccno, -1 * detail.amount)
+                    }
+                    if (TradeDict.SUBJNO_SHOP == detail.crsubjno) {
+                        doDealShopacc(detail.draccno, detail.amount)
+                    }
+                }
+
+                userdtl.status = TradeDict.DTL_STATUS_SUCCESS
+                userdtl.accdate = systemUtilService.accdate //入账成功时更新
+                userdtl.endtime = systemUtilService.sysdatetime.hostdatetime
+                //TODO 存储一些业务参数
+                return userdtlDao.save(userdtl)
+            }
+
+            else -> throw TransactionProcessException(TradeErrorCode.TRANSDTL_STATUS_ERROR, "未指定明确的交易结束状态")
+        }
+    }
+
+    override fun wip(paydtl: TUserdtl, builder: PersonTransBuilder): TUserdtl {
+        return wip(paydtl.refno, builder)
+    }
+
+    override fun wip(refno: String, builder: PersonTransBuilder): TUserdtl {
+        var userdtl = getLockUserdtlNowait(refno)
+        if (TradeDict.DTL_STATUS_INIT != userdtl.status)
+            throw TransactionProcessException(TradeErrorCode.TRANSDTL_STATUS_NOT_INIT, "交易参考号<$refno>非初始化流水")
+
+        userdtl.status = TradeDict.DTL_STATUS_WIP  //待支付
+        return userdtlDao.save(userdtl)
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/consume/service/pay_service.kt b/src/main/kotlin/com/supwisdom/dlpay/consume/service/pay_service.kt
index d3d9933..8bfc900 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/consume/service/pay_service.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/consume/service/pay_service.kt
@@ -34,16 +34,18 @@
     fun init(builder: PersonTransBuilder): TUserdtl
 
     /**
+     * 两步交易,完成交易过程,包括更新交易状态(成功、失败),更新借贷双方余额
+     */
+    fun finish(paydtl: TUserdtl, status: String, businessData: Map<String, String>?): TUserdtl
+
+    fun finish(refno: String, status: String, businessData: Map<String, String>?): TUserdtl
+
+    /**
      * 两步交易,交易过程中判断交易状态,并更新交易状态为 wip
      */
     fun wip(paydtl: TUserdtl, builder: PersonTransBuilder): TUserdtl
 
     fun wip(refno: String, builder: PersonTransBuilder): TUserdtl
 
-    /**
-     * 两步交易,完成交易过程,包括更新交易状态(成功、失败),更新借贷双方余额
-     */
-    fun finish(paydtl: TUserdtl, status: String, builder: PersonTransBuilder): TUserdtl
 
-    fun finish(refno: String, status: String, builder: PersonTransBuilder): TUserdtl
 }
\ No newline at end of file