充值记流水
diff --git a/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java b/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java
index 649758d..476a1fa 100644
--- a/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java
+++ b/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java
@@ -15,4 +15,10 @@
   @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "0")})
   @Query(value = "select t from TTransactionMain t where t.refno = ?1")
   TTransactionMain findByRefnoForUpdate(String refno);
+
+
+  TTransactionMain findByRefno(String refno);
+
+  @Query("select t from TTransactionMain t where t.outTradeNo=?1 and t.outId=?2")
+  TTransactionMain findByBillno(String billno, String outid);
 }
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/Subject.java b/src/main/java/com/supwisdom/dlpay/framework/util/Subject.java
index 4a46b28..e80344a 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/Subject.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/Subject.java
@@ -28,6 +28,11 @@
   public static final String SUBJNO_RECHARGE_CASH = "112201";
 
   /**
+   * 应收账款 - 其他第三方充值款
+   */
+  public static final String SUBJNO_RECHARGE_OTHER = "112209";
+
+  /**
    * 应收账款 - 支付宝充值款
    */
   public static final String SUBJNO_RECHARGE_ALIPAY = "112210";
diff --git a/src/main/java/com/supwisdom/dlpay/framework/util/TradeCode.java b/src/main/java/com/supwisdom/dlpay/framework/util/TradeCode.java
index 20cd922..cc1b344 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/TradeCode.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/TradeCode.java
@@ -12,6 +12,9 @@
   public static final int TRANSCODE_WECHAT=1001;
 
 
-  public static final int TRANSTYPE_PAY = 311;
-  public static final int TRANSTYPE_YKTPAY=1000;
+  public static final int TRANSCODE_ERCHARGE = 3500; //账户充值
+
+
+
+
 }
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 2928642..a72913a 100644
--- a/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
+++ b/src/main/java/com/supwisdom/dlpay/framework/util/TradeDict.java
@@ -68,6 +68,7 @@
   public static final String PAYTYPE_WECHAT = "wechat"; //市民卡
   public static final String PAYTYPE_CITIZEN_CARD = "citizenCard"; //市民卡
   public static final String PAYTYPE_YKT_CARD = "yktpay"; //一卡通
+  public static final String PAYTYPE_OTHER_THIRDPART = "thirdpart"; //其他第三方
 
 
   /**
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/bean/api_request_param.kt b/src/main/kotlin/com/supwisdom/dlpay/api/bean/api_request_param.kt
index 6e06756..aab9a8d 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/bean/api_request_param.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/bean/api_request_param.kt
@@ -250,4 +250,57 @@
 
 
 
+// ============================ RECHARGE ============================ //
+class CommonRechargeInitParam : APIRequestParam() {
+    @Sign
+    var userid: String = "" //用户ID
+    @Sign
+    var amount: Int = 0 //必传
+
+    var feelist: List<ConsumeFeetype>? = null //TODO: 怎么拼接签名字符串??
+    @Sign
+    var sourcetype: String = "" //必传,充值方式
+    @Sign
+    var billno: String = "" //必传
+    @Sign
+    var transdate: String = "" //必传
+    @Sign
+    var transtime: String = "" //必传
+
+    override fun checkParam(): Boolean {
+        if (StringUtil.isEmpty(userid)) throw RequestParamCheckException(TradeErrorCode.REQUEST_PARAM_ERROR, "请指定充值用户")
+        if (amount <= 0) throw RequestParamCheckException(TradeErrorCode.REQUEST_PARAM_ERROR, "充值金额必须大于零")
+        if (StringUtil.isEmpty(sourcetype)) throw RequestParamCheckException(TradeErrorCode.REQUEST_PARAM_ERROR, "请指定充值的支付方式")
+        if (StringUtil.isEmpty(billno)) throw RequestParamCheckException(TradeErrorCode.REQUEST_PARAM_ERROR, "对接系统唯一订单号不能为空")
+        if (!DateUtil.checkDatetimeValid(transdate, DateUtil.DATE_FMT)) throw RequestParamCheckException(TradeErrorCode.REQUEST_PARAM_ERROR, "交易日期错误[yyyyMMdd]")
+        if (!DateUtil.checkDatetimeValid(transtime, DateUtil.TIME_FMT)) throw RequestParamCheckException(TradeErrorCode.REQUEST_PARAM_ERROR, "交易时间错误[HHmmss]")
+
+        return true
+    }
+}
+
+class CommonRechargeConfirmParam : APIRequestParam() {
+    @Sign
+    var refno:String = "" //流水号
+
+    override fun checkParam(): Boolean {
+        if(StringUtil.isEmpty(refno)) throw RequestParamCheckException(TradeErrorCode.REQUEST_PARAM_ERROR, "流水号不能为空")
+
+        return true
+    }
+}
+
+class CommonQueryRechargeResultParam : APIRequestParam() {
+    @Sign
+    var refno: String? = null //流水号
+    @Sign
+    var billno: String? = null
+
+    override fun checkParam(): Boolean {
+        if (StringUtil.isEmpty(refno) || StringUtil.isEmpty(billno)) throw RequestParamCheckException(TradeErrorCode.REQUEST_PARAM_ERROR, "流水号不能为空")
+
+        return true
+    }
+}
+
 
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/controller/charge_api_controller.kt b/src/main/kotlin/com/supwisdom/dlpay/api/controller/charge_api_controller.kt
new file mode 100644
index 0000000..cb5ad33
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/controller/charge_api_controller.kt
@@ -0,0 +1,156 @@
+package com.supwisdom.dlpay.api.controller
+
+import com.supwisdom.dlpay.api.AccountProxy
+import com.supwisdom.dlpay.api.TransactionBuilder
+import com.supwisdom.dlpay.api.bean.*
+import com.supwisdom.dlpay.api.service.AccountUtilServcie
+import com.supwisdom.dlpay.api.service.ChargeApiService
+import com.supwisdom.dlpay.api.service.TransactionService
+import com.supwisdom.dlpay.api.service.UserService
+import com.supwisdom.dlpay.exception.TransactionCheckException
+import com.supwisdom.dlpay.exception.TransactionProcessException
+import com.supwisdom.dlpay.framework.ResponseBodyBuilder
+import com.supwisdom.dlpay.framework.service.CommonService
+import com.supwisdom.dlpay.framework.service.SystemUtilService
+import com.supwisdom.dlpay.framework.util.Subject
+import com.supwisdom.dlpay.framework.util.TradeCode
+import com.supwisdom.dlpay.framework.util.TradeDict
+import com.supwisdom.dlpay.framework.util.TradeErrorCode
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.http.ResponseEntity
+import org.springframework.security.core.Authentication
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/api/recharge")
+class ChargeAPIController {
+    @Autowired
+    lateinit var accountUtilServcie: AccountUtilServcie
+    @Autowired
+    lateinit var systemUtilService: SystemUtilService
+    @Autowired
+    lateinit var transactionService: TransactionService
+    @Autowired
+    lateinit var commonService: CommonService
+    @Autowired
+    lateinit var userService: UserService
+    @Autowired
+    lateinit var chargeApiService: ChargeApiService
+
+
+    /**
+     * ============================================================================
+     * 通用充值(只记流水)
+     * ============================================================================
+     * */
+    @PostMapping("/common/init")
+    fun rechargeInit(@RequestBody param: CommonRechargeInitParam, authentication: Authentication): ResponseEntity<Any> {
+        if (param.checkSign(commonService.getSecretByAppid(authentication.name))) {
+            return ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .fail(TradeErrorCode.REQUEST_SIGN_ERROR, "参数签名错误"))
+        }
+
+        if(chargeApiService.checkRechargeSourcetype(param.sourcetype)) {
+            val person = userService.findOnePersonByUserid(param.userid)
+            val account = accountUtilServcie.readAccount(person.userid)
+            val rechargeDrsubjno = when (param.sourcetype) {
+                //根据不同支付方式确定借方科目,fixme:可根据outid或clientId自定义
+
+                TradeDict.PAYTYPE_CASH -> accountUtilServcie.readSubject(Subject.SUBJNO_RECHARGE_CASH)
+                TradeDict.PAYTYPE_OTHER_THIRDPART -> accountUtilServcie.readSubject(Subject.SUBJNO_RECHARGE_OTHER)
+                else -> throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR, "充值支付方式<${param.sourcetype}>未确定所属科目")
+            }
+
+            val transaction = TransactionBuilder().apply {
+                setTransInfo(param.transdate, param.transtime,
+                        TradeCode.TRANSCODE_ERCHARGE,
+                        param.sourcetype)
+                setOutTransInfo(authentication.name, param.billno) //fixme: outid取clientId
+//                operator("", TradeDict.OPERTYPE_OPER) //充值操作员
+                payinfo = ""
+                description = "账户充值"
+            }.person(account).apply {
+                setAmount(param.amount / 100.0, TradeDict.TRADE_FLAG_IN)
+                this.opposite = AccountProxy(rechargeDrsubjno)
+            }.and().addDebitCreditRecord(AccountProxy(rechargeDrsubjno), AccountProxy(account),
+                    param.amount / 100.0, "账户充值")
+                    .also { builder ->
+                        param.feelist?.forEach {
+                            val feeamt = it.amount / 100.0
+                            when (it.feetype) {
+                                TradeDict.PAYTYPE_RECHARGE_COUPON -> {
+                                    //优惠折扣  优惠科目 -> 个人账户
+                                    val feetypeConfig = accountUtilServcie.readFeetype(TradeDict.PAYTYPE_RECHARGE_COUPON, param.sourcetype)
+                                    val subject = accountUtilServcie.readSubject(feetypeConfig.drsubjno) //不同在借方折扣科目
+                                    builder.addDebitCreditRecord(AccountProxy(subject), AccountProxy(account), feeamt, feetypeConfig.summary)
+                                }
+                                //fixme: 服务费暂缺
+//                                TradeDict.PAYTYPE_RECHARGE_SERVICEFEE -> {
+//                                    //收服务费 借方 -> 服务费科目
+//                                    val feetypeConfig = accountUtilServcie.readFeetype(TradeDict.PAYTYPE_RECHARGE_SERVICEFEE, param.sourcetype)
+//                                    val subject = accountUtilServcie.readSubject(feetypeConfig.crsubjno) //不同在服务费放在贷方科目
+//                                    builder.addDebitCreditRecord(AccountProxy(rechargeDrsubjno), AccountProxy(subject), feeamt, feetypeConfig.summary)
+//
+//                                }
+                                else -> throw TransactionCheckException(TradeErrorCode.INPUT_DATA_ERROR, "充值费用清单不支持feetype<${it.feetype}>")
+                            }
+                        }
+                    }.init(transactionService)
+
+            return ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .data("refno", transaction.refno)
+                    .data("amount", transaction.personDtl.amount)
+                    .success("初始化成功"))
+
+        }
+
+        return ResponseEntity.ok(ResponseBodyBuilder.create()
+                .fail(TradeErrorCode.BUSINESS_PAYTYPE_NOSUPPORT, "充值不支持支付方式<${param.sourcetype}>"))
+    }
+
+    @PostMapping("/common/confirm")
+    fun rechargeConfirm(@RequestBody param: CommonRechargeConfirmParam, authentication: Authentication): ResponseEntity<Any> {
+        if (param.checkSign(commonService.getSecretByAppid(authentication.name))) {
+            return ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .fail(TradeErrorCode.REQUEST_SIGN_ERROR, "参数签名错误"))
+        }
+
+        transactionService.success(param.refno).let {
+            return ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .data("refno", it.refno)
+                    .data("billno", it.outTradeNo)
+                    .success("交易确认成功"))
+        }
+
+    }
+
+    /**
+     * ============================================================================
+     * 查询充值流水状态
+     * ============================================================================
+     * */
+    @PostMapping("/queryresult")
+    fun rechargeConfirm(@RequestBody param: CommonQueryRechargeResultParam, authentication: Authentication): ResponseEntity<Any> {
+        if (param.checkSign(commonService.getSecretByAppid(authentication.name))) {
+            return ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .fail(TradeErrorCode.REQUEST_SIGN_ERROR, "参数签名错误"))
+        }
+
+        chargeApiService.getTransactionMainDtl(param.refno, param.billno, authentication.name).let {
+            if (null == it) {
+                return ResponseEntity.ok(ResponseBodyBuilder.create()
+                        .fail(TradeErrorCode.TRANSACTION_NOT_EXISTS, "充值流水不存在"))
+            }
+
+            return ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .data("refno", it.refno)
+                    .data("billno", it.outTradeNo)
+                    .data("status", it.status)
+                    .success("查询成功"))
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt b/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
index 4a8241d..c1ed619 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
@@ -30,7 +30,7 @@
 
 @RestController
 @RequestMapping("/api/consume")
-class ConsumeController {
+class ConsumeAPIController {
     @Autowired
     lateinit var accountUtilServcie: AccountUtilServcie
     @Autowired
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/charge_api_service.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/charge_api_service.kt
new file mode 100644
index 0000000..e0e010e
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/charge_api_service.kt
@@ -0,0 +1,13 @@
+package com.supwisdom.dlpay.api.service
+
+import com.supwisdom.dlpay.api.domain.TTransactionMain
+import org.springframework.transaction.annotation.Propagation
+import org.springframework.transaction.annotation.Transactional
+
+interface ChargeApiService {
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class], readOnly = true)
+    fun checkRechargeSourcetype(sourceType: String): Boolean
+
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class], readOnly = true)
+    fun getTransactionMainDtl(refno: String?, billno: String?, outid: String?): TTransactionMain?
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/consume_pay_service.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/consume_pay_service.kt
index 412fdf5..1fa74f3 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/service/consume_pay_service.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/consume_pay_service.kt
@@ -4,12 +4,12 @@
 import org.springframework.transaction.annotation.Transactional
 
 interface ConsumePayService {
-    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class], readOnly = true)
     fun checkShopPaytype(shopaccno: String, sourceType: String, anonymousflag: Boolean? = false): Boolean
 
-    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class], readOnly = true)
     fun getPaytypeConfig(paytype: String, shopaccno: String, anonymousflag: Boolean? = false, ignoreStatus: Boolean? = false): Map<String, String?>
 
-    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class])
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = [Exception::class], readOnly = true)
     fun getUserdtlExtendParamMap(refno: String): Map<String, String>
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/charge_api_service_impl.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/charge_api_service_impl.kt
new file mode 100644
index 0000000..7c393a8
--- /dev/null
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/charge_api_service_impl.kt
@@ -0,0 +1,40 @@
+package com.supwisdom.dlpay.api.service.impl
+
+import com.supwisdom.dlpay.api.dao.SourceTypeDao
+import com.supwisdom.dlpay.api.dao.TransactionMainDao
+import com.supwisdom.dlpay.api.domain.TTransactionMain
+import com.supwisdom.dlpay.api.service.ChargeApiService
+import com.supwisdom.dlpay.exception.TransactionProcessException
+import com.supwisdom.dlpay.framework.util.StringUtil
+import com.supwisdom.dlpay.framework.util.TradeErrorCode
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+
+@Service
+class ChargeApiServiceImpl : ChargeApiService {
+    @Autowired
+    lateinit var sourceTypeDao: SourceTypeDao
+    @Autowired
+    lateinit var transactionMainDao: TransactionMainDao
+
+    override fun checkRechargeSourcetype(sourceType: String): Boolean {
+        sourceTypeDao.getBySourceType(sourceType).let {
+            if (null == it) {
+                throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR, "系统不支持支付方式[$sourceType]")
+            } else {
+                if (!it.enable || !it.isChargeEnable) {
+                    throw TransactionProcessException(TradeErrorCode.INPUT_DATA_ERROR, "系统充值未启用支付方式[$sourceType]")
+                }
+            }
+        }
+
+        return true
+    }
+
+    override fun getTransactionMainDtl(refno: String?, billno: String?, outid: String?): TTransactionMain? {
+        return when (!StringUtil.isEmpty(refno)) {
+            true -> transactionMainDao.findByRefno(refno)
+            false -> transactionMainDao.findByBillno(billno, outid)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/Consume_pay_service_impl.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/consume_pay_service_impl.kt
similarity index 100%
rename from src/main/kotlin/com/supwisdom/dlpay/api/service/impl/Consume_pay_service_impl.kt
rename to src/main/kotlin/com/supwisdom/dlpay/api/service/impl/consume_pay_service_impl.kt
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
index fa30ada..91fef26 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/user_service_impl.kt
@@ -167,4 +167,9 @@
         }
     }
 
+    override fun findOnePersonByUserid(userid: String): TPerson {
+        return personDao.findByUserid(userid)
+                ?: throw TransactionProcessException(TradeErrorCode.ACCOUNT_NOT_EXISTS, "用户<$userid>不存在")
+    }
+
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/supwisdom/dlpay/api/service/user_service.kt b/src/main/kotlin/com/supwisdom/dlpay/api/service/user_service.kt
index c4d9b20..6ac023e 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/api/service/user_service.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/api/service/user_service.kt
@@ -40,4 +40,7 @@
     @Transactional(propagation = Propagation.REQUIRED, rollbackFor = arrayOf(Exception::class), readOnly = true)
     fun findPersonByIdentityCheckStatus(thirdUniqueIdenty: String): TPerson
 
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = arrayOf(Exception::class), readOnly = true)
+    fun findOnePersonByUserid(userid: String): TPerson
+
 }
\ No newline at end of file
diff --git a/src/main/resources/templates/ologin.html b/src/main/resources/templates/ologin.html
index 41302e1..8f9362e 100644
--- a/src/main/resources/templates/ologin.html
+++ b/src/main/resources/templates/ologin.html
@@ -85,6 +85,11 @@
         base: 'custom/module/'
     }).use(['form'], function () {
         var $ = layui.jquery;
+
+        // 图形验证码
+        $('.login-captcha').click(function () {
+            this.src = this.src + '?t=' + (new Date).getTime();
+        });
     });
 </script>
 </body>