From 47277e68c1de4be3412f4988a2d1d1713e391877 Mon Sep 17 00:00:00 2001 From: Tang Cheng Date: Mon, 20 May 2019 23:28:43 +0800 Subject: [PATCH] =?utf8?q?=E4=B8=B4=E6=97=B6=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../supwisdom/dlpay/api/dao/AccountDao.java | 15 +- .../supwisdom/dlpay/api/dao/PersondtlDao.java | 7 + .../supwisdom/dlpay/api/dao/ShopdtlDao.java | 7 + .../dlpay/api/dao/SubjectdtlDao.java | 7 + .../dlpay/api/dao/TransactionMainDao.java | 18 ++ .../dlpay/api/domain/TDebitCreditDtl.java | 13 +- .../dlpay/api/domain/TPersondtl.java | 38 ++- .../supwisdom/dlpay/api/domain/TShopdtl.java | 43 ++- .../dlpay/api/domain/TSubjectdtl.java | 153 ++++++++++ .../dlpay/api/domain/TTransactionMain.java | 136 +++++++++ .../api/repositories/TAccountRepository.java | 5 + .../impl/TAccountRepositoryImpl.java | 31 ++ .../dlpay/framework/util/TradeDict.java | 1 + .../service/impl/transaction_service_impl.kt | 282 ++++++++++++++++++ .../dlpay/api/service/transaction_service.kt | 19 +- .../dlpay/api/transaction_builder.kt | 163 +++++++--- 16 files changed, 872 insertions(+), 66 deletions(-) create mode 100644 src/main/java/com/supwisdom/dlpay/api/dao/PersondtlDao.java create mode 100644 src/main/java/com/supwisdom/dlpay/api/dao/ShopdtlDao.java create mode 100644 src/main/java/com/supwisdom/dlpay/api/dao/SubjectdtlDao.java create mode 100644 src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java create mode 100644 src/main/java/com/supwisdom/dlpay/api/domain/TSubjectdtl.java create mode 100644 src/main/java/com/supwisdom/dlpay/api/domain/TTransactionMain.java create mode 100644 src/main/java/com/supwisdom/dlpay/api/repositories/TAccountRepository.java create mode 100644 src/main/java/com/supwisdom/dlpay/api/repositories/impl/TAccountRepositoryImpl.java create mode 100644 src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt 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 806b049c..8d3130db 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 { +public interface AccountDao extends JpaRepository, 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 @@ public interface AccountDao extends JpaRepository { @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 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 00000000..2ee45500 --- /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 { +} 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 00000000..42f928fa --- /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 { +} 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 00000000..968a0310 --- /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 { +} 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 00000000..649758dc --- /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 { + + @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 dc5a9a96..85bac09f 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 @@ public class TDebitCreditDtl { 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 class TDebitCreditDtl { 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 deaeaf34..0e37a32e 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 @@ import javax.persistence.*; 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 @@ public class TPersondtl { @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 @@ public class TPersondtl { @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 class TPersondtl { 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 2649d023..ad43a2f4 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 @@ public class TShopdtl { @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 @@ public class TShopdtl { @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 class TShopdtl { 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 00000000..dc3fe04b --- /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 00000000..dba79482 --- /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 00000000..d999a27c --- /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 00000000..029e85f5 --- /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 7bec4217..8769f1f1 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 class TradeDict { 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 00000000..4359f7f3 --- /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, + 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 c9e940ac..b4407c16 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 0a7b86f5..68c639a2 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<*> +} + +class SubjectTransactionBuilder(parent: TransactionBuilder, val subject: TSubject) + : SubTransactionBuilder(parent) { + var amount: Double = 0.0 + var summary: String = "" + lateinit var opposite: AccountProxy<*> - fun and(): TransactionBuilder { - return parent - } } class AccountProxy(private val accountObj: T) { @@ -81,6 +78,10 @@ class AccountProxy(private val accountObj: T) { "不支持的账户类型, $accountObj") } } + + fun get(): T { + return accountObj + } } data class DebitCreditLine(val debit: AccountProxy<*>, val credit: AccountProxy<*>, @@ -99,28 +100,66 @@ class TransactionBuilder { var transTime: String = "" var transCode: Int = 0 //交易码,各明细流水统一 var refno: String = "" + var paytype: String = "" + var payinfo: String = "" var outtradeno: String = "" //第三方流水号 val extendMap = mutableMapOf() //存调第三方需要的参数信息,存数据库 TB_USERDTL_BUSINESS val resultMap = mutableMapOf() //存调第三方结果数据 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 subject(): SubjectTransactionBuilder { - return SubjectTransactionBuilder(this).also { - this.subjectBuilder = it + 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 this.subjectBuilder + } + + fun hasSubject(): Boolean { + return this::subjectBuilder.isInitialized + } + fun addDebitCreditRecord(debit: AccountProxy, credit: AccountProxy, amount: Double, summary: String): TransactionBuilder { debitCreditLines.add(DebitCreditLine(debit, credit, amount, summary, @@ -128,17 +167,36 @@ class TransactionBuilder { return this } - fun init(transactionService: PersonBalancePayService) { + fun getAllDetails(): List { + 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() +} + 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 @@ class TransactionExample { 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 @@ class TransactionExample { 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 @@ class TransactionExample { 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 @@ class TransactionExample { } 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 -- 2.17.1