新接口测试通过
diff --git a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/TransactionProxy.java b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/TransactionProxy.java
index 570587a..5d6be32 100644
--- a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/TransactionProxy.java
+++ b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/TransactionProxy.java
@@ -15,4 +15,7 @@
 
   @PostMapping("/api/consume/queryresult")
   QueryTransDtlResponse queryDtlResult(@RequestBody QueryDtlResultParam param);
+
+  @PostMapping("/api/consume/thirdquery")
+  QueryTransDtlResponse queryDtlResultByThird(@RequestBody QueryDtlResultParam param);
 }
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayChkfile.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayChkfile.java
new file mode 100644
index 0000000..1690847
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayChkfile.java
@@ -0,0 +1,138 @@
+package com.supwisdom.dlpay.agent.ynrccpay;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 农商行快捷支付对账单
+ * 	4
+ * ZF01|20210308|20201225161726|20210308151427000066|5607894487|10020104721000000001||0.01|156|0|0.01||20201225161726000115
+ * ZF01|20210308|20210308161616|20210308140757000064|5607894462|10020104721000000001||0.01|156|0|0.01||20210113170353000126
+ * ZF01|20210308|20210308181818|20210308150900000065|5607894483|10020104721000000001||0.01|156|0|0.01||20201225110202000098
+ * ZF02|20210308|20210308152004|114075700006420210308152004|20210308140757000064|10020104721000000001||0.01|156|0|0.01||
+ * */
+public class YnrccPayChkfile {
+
+  private String payFlag; //交易代码:ZF01-支付;ZF02-退款
+  private String clearDate; //清算日期
+  private String transDateTime; //交易时间 yyyyMMddHHmmss
+  private String billNo; //商户订单号
+  private String agentRefno; //银行流水号
+  private String merchantNo; //商户号
+  private String termNo; //终端号
+  private String amount; //交易金额,单位:元
+  private String currencyType; //币种  156-人民币
+  private String feeAmount; //手续费,单位:元
+  private String diffAmount; //交易金额与手续费金额的差值(交易金额-手续费金额),单位:元
+  private String remark1; //备注1
+  private String remark2; //备注2
+
+  public static final String PAY = "ZF01";
+  public static final String REFUND = "ZF02";
+  public static final List<String> HEADER = Arrays.asList("payFlag", "clearDate", "transDateTime", "billNo", "agentRefno",
+      "merchantNo", "termNo", "amount", "currencyType", "feeAmount", "diffAmount", "remark1", "remark2");
+
+  public String getPayFlag() {
+    return payFlag;
+  }
+
+  public void setPayFlag(String payFlag) {
+    this.payFlag = payFlag;
+  }
+
+  public String getClearDate() {
+    return clearDate;
+  }
+
+  public void setClearDate(String clearDate) {
+    this.clearDate = clearDate;
+  }
+
+  public String getTransDateTime() {
+    return transDateTime;
+  }
+
+  public void setTransDateTime(String transDateTime) {
+    this.transDateTime = transDateTime;
+  }
+
+  public String getBillNo() {
+    return billNo;
+  }
+
+  public void setBillNo(String billNo) {
+    this.billNo = billNo;
+  }
+
+  public String getAgentRefno() {
+    return agentRefno;
+  }
+
+  public void setAgentRefno(String agentRefno) {
+    this.agentRefno = agentRefno;
+  }
+
+  public String getMerchantNo() {
+    return merchantNo;
+  }
+
+  public void setMerchantNo(String merchantNo) {
+    this.merchantNo = merchantNo;
+  }
+
+  public String getTermNo() {
+    return termNo;
+  }
+
+  public void setTermNo(String termNo) {
+    this.termNo = termNo;
+  }
+
+  public String getAmount() {
+    return amount;
+  }
+
+  public void setAmount(String amount) {
+    this.amount = amount;
+  }
+
+  public String getCurrencyType() {
+    return currencyType;
+  }
+
+  public void setCurrencyType(String currencyType) {
+    this.currencyType = currencyType;
+  }
+
+  public String getFeeAmount() {
+    return feeAmount;
+  }
+
+  public void setFeeAmount(String feeAmount) {
+    this.feeAmount = feeAmount;
+  }
+
+  public String getDiffAmount() {
+    return diffAmount;
+  }
+
+  public void setDiffAmount(String diffAmount) {
+    this.diffAmount = diffAmount;
+  }
+
+  public String getRemark1() {
+    return remark1;
+  }
+
+  public void setRemark1(String remark1) {
+    this.remark1 = remark1;
+  }
+
+  public String getRemark2() {
+    return remark2;
+  }
+
+  public void setRemark2(String remark2) {
+    this.remark2 = remark2;
+  }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayUtil.java
index 38a6982..ce51759 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayUtil.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/ynrccpay/YnrccPayUtil.java
@@ -17,10 +17,10 @@
   public static final String MER_NAME = "ynrcc.netpay.Mer_IdName";
   public static final String MER_URL = "ynrcc.netpay.MerURL";
 
-
+  public static final String chkFileDelimiter = "|";
   public static final String SUCCESS = "ZF0000";
   public static final String WAIT_FOR_QUERY = "ZF0098"; //等待查询结果
-  public static final String NO_RECORDS_TODAY = "0406"; //当日无交易明细
+  public static final String NO_RECORDS_TODAY = "ZF0101"; //当日无交易明细
 
 
   public static String toJson(Object obj) {
@@ -76,12 +76,13 @@
 
     errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZF00A0", "成功接收订单")));
     errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZF00A1", "支付方取消订单")));
-    errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZF0101", "无相关数据")));
+    errcode.add(Pair.of(AgentCode.REFNO_NOT_EXISTS, new YnrccRespCode("ZF0101", "无相关数据")));
     errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZF0214", "平台已冻结或已注销")));
 
     //自定义错误 ZExxxx
     errcode.add(Pair.of(AgentCode.CONFIG_ERROR, new YnrccRespCode("ZE0001", "系统参数未配置")));
     errcode.add(Pair.of(AgentCode.COMMON_ERROR, new YnrccRespCode("ZE0002", "签名计算错误")));
+    errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZE0097", "请求农商行网关快捷支付返回数据有误")));
     errcode.add(Pair.of(AgentCode.ILLEGAL_DATA, new YnrccRespCode("ZE0098", "请求农商行网关快捷支付返回数据为空")));
     errcode.add(Pair.of(AgentCode.SERVER_INTERNAL_ERROR, new YnrccRespCode("ZE0099", "请求农商行网关快捷支付httpStatus非200")));
   }
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/ShopSourceTypeConfigDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/ShopSourceTypeConfigDao.java
index e861165..9c59a82 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/ShopSourceTypeConfigDao.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/ShopSourceTypeConfigDao.java
@@ -14,4 +14,7 @@
 
   @Query("select a from TShopSourceTypeConfig a where a.shopaccno=?1 and a.sourceType=?2 and a.configid=?3 ")
   TShopSourceTypeConfig getShopSourceTypeConfigById(String shopaccno, String sourceType, String configid);
+
+  @Query("select distinct a.shopaccno from TShopSourceTypeConfig a where a.sourceType=?1 order by a.shopaccno ")
+  List<String> getSourcetypeBindShopaccnos(String sourceType);
 }
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java
index b72f8d2..10a3139 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionMainDao.java
@@ -8,6 +8,7 @@
 
 import javax.persistence.LockModeType;
 import javax.persistence.QueryHint;
+import java.util.List;
 
 public interface TransactionMainDao extends CrudRepository<TTransactionMain, String> {
 
@@ -28,4 +29,12 @@
 
   @Query("select min(t.accdate) from TTransactionMain t where t.sourceType=?1 ")
   String findMinAccdateBySourcetype(String sourcetype);
+
+  @Query("from TTransactionMain t where t.reverseRefno=?1 and t.status='success' order by t.refno ")
+  List<TTransactionMain> findByRefundSuccessTransdtl(String refno);
+
+  @Query("from TTransactionMain t where t.reverseRefno=?1 order by t.refno ")
+  List<TTransactionMain> findByRefundTransdtl(String refno);
+
+
 }
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/service/SourceTypeService.java b/payapi/src/main/java/com/supwisdom/dlpay/api/service/SourceTypeService.java
index 8e4048b..0e81d21 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/service/SourceTypeService.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/service/SourceTypeService.java
@@ -52,4 +52,7 @@
   @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
   TSourceTypeCheckStatus saveOrUpdateSourceTypeCheckStatus(TSourceTypeCheckStatus s);
 
+  @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
+  List<String> getShopaccnoBySourcetype(String sourcetype);
+
 }
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java b/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java
index 62c471f..f0bd529 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/service/impl/SourceTypeServiceImpl.java
@@ -11,6 +11,7 @@
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -210,4 +211,10 @@
     }
     return sourceTypeCheckDao.save(s);
   }
+
+  @Override
+  public List<String> getShopaccnoBySourcetype(String sourcetype) {
+    List<String> list = shopSourceTypeConfigDao.getSourcetypeBindShopaccnos(sourcetype);
+    return list != null ? list : new ArrayList<String>(0);
+  }
 }
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/ynrcc_netpay_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/ynrcc_netpay_service_impl.kt
index 3949c55..d8b5af1 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/ynrcc_netpay_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/impl/ynrcc_netpay_service_impl.kt
@@ -22,6 +22,7 @@
 import org.springframework.util.LinkedMultiValueMap
 import org.springframework.util.MultiValueMap
 import org.springframework.web.client.RestTemplate
+import java.net.URLDecoder
 import java.nio.charset.StandardCharsets
 
 @Service
@@ -186,7 +187,10 @@
                 return error("ZE0098", "请求农商行网关【$opt】返回数据为空!")
             }
 
-            val resData = YNRCCFastGateWayPayApi.verify(resJson.signature, resJson.plain) //调SDK验证签名
+            val resData = when ("对账" == opt) {
+                true -> YNRCCFastGateWayPayApi.verify(resJson.signature, URLDecoder.decode(resJson.plain,"UTF-8")) //对账需先解码
+                false -> YNRCCFastGateWayPayApi.verify(resJson.signature, resJson.plain) //调SDK验证签名
+            }
             logger.info("农商行网关快捷支付请求【$opt】返回:\nurl=[$url]\nResponse Plain=[" + YnrccPayUtil.toJson(resData) + "]")
             val respCode = resData["RespCode"]?.trim()?.replace("\r\n", "")
             return if (YnrccPayUtil.SUCCESS == respCode) {
@@ -421,7 +425,7 @@
         resp.code = result["RespCode"]
         resp.message = result["RespDesc"]
         if (YnrccPayUtil.SUCCESS == result["RespCode"]) {
-            resp.plain = result["Plain"] //对账单
+            resp.plain = result["FileContent"] //对账单
         }
         return resp
     }
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrcc_netpay_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrcc_netpay_service.kt
index 127e319..2510ab4 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrcc_netpay_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrcc_netpay_service.kt
@@ -86,7 +86,7 @@
     }
 
     override fun query(transaction: TTransactionMain): AgentResponse<DtlStatus> {
-        val resp = ynrccNetPayService.doQueryYnrccPayResult(transaction.shopDtl.shopaccno, transaction.refno, transaction.shopDtl.transdate)
+        val resp = ynrccNetPayService.doQueryYnrccPayResult(transaction.shopDtl.shopaccno, transaction.refno, transaction.shopDtl.accdate)
 
         return AgentResponse<DtlStatus>().also {
             val code = agentCode(resp.code, resp.message)
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrccpay_checkfile_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrccpay_checkfile_service.kt
index 0c7126e..21fc8c1 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrccpay_checkfile_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/ynrccpay_checkfile_service.kt
@@ -3,6 +3,7 @@
 import com.supwisdom.dlpay.agent.AgentCode
 import com.supwisdom.dlpay.agent.AgentResponse
 import com.supwisdom.dlpay.agent.CheckFileProvider
+import com.supwisdom.dlpay.agent.ynrccpay.YnrccPayChkfile
 import com.supwisdom.dlpay.agent.ynrccpay.YnrccPayUtil
 import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
 import com.supwisdom.dlpay.api.domain.TTransactionChkfile
@@ -10,10 +11,16 @@
 import com.supwisdom.dlpay.api.service.TransactionReconciliationService
 import com.supwisdom.dlpay.exception.TransactionException
 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.util.ConstantUtil
 import mu.KotlinLogging
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.stereotype.Component
+import java.io.BufferedReader
+import java.io.ByteArrayInputStream
+import java.io.InputStreamReader
+import java.nio.charset.Charset
 
 @Component("ynrccpayCheckFileProvider")
 class YnrccpayCheckFileProvider : CheckFileProvider {
@@ -58,49 +65,89 @@
                 return successDownload("[$billDate]日对账数据已入库成功", checkStatus)
             }
 
-            val shopaccno = ""
-            val resp = ynrccNetPayService.doQueryYnrccPayChkfile(billDate, shopaccno)
-            when (resp.code) {
-                YnrccPayUtil.SUCCESS -> {
-                    //TODO: 农商行网关快捷支付 对账文件解析
-                    val agentUrl = ""
-
-                    //成功
-                    return AgentResponse<TSourceTypeCheckStatus>().also {
-                        it.code = AgentCode.FAIL
-                        it.agentMsg = "获取对账文件逻辑暂缺"
-//                        it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
-//                            this.checkFileOk = false
-//                            this.chkfileUrl = agentUrl
-//                        })
+            val fileDataList = ArrayList<YnrccPayChkfile>(0)
+            val shopaccList = sourceTypeService.getShopaccnoBySourcetype(TradeDict.PAYTYPE_YNRCC_PAY) //所有开通快捷支付的商户
+            for (shopacc in shopaccList) {
+                val resp = ynrccNetPayService.doQueryYnrccPayChkfile(billDate, shopacc)
+                when (resp.code) {
+                    YnrccPayUtil.SUCCESS -> {
+                        //成功
+                        BufferedReader(InputStreamReader(resp.plain?.byteInputStream(Charset.forName("UTF-8")))).use { reader ->
+                            val totalCnt = reader.readLine()?.trim()?.toInt() //总条目数
+                            var records = 0
+                            while (true) {
+                                val line = reader.readLine() ?: break
+                                if (line.isEmpty()) continue
+                                val columns = line.split(YnrccPayUtil.chkFileDelimiter)
+                                val bean = YnrccPayChkfile()
+                                try {
+                                    StringUtil.transforToBean(YnrccPayChkfile.HEADER, columns, bean)
+                                }catch (etr:Exception){
+                                    throw TransactionException(97, "商户[$shopacc]对账单下载[$billDate]文件明细解析错误!${etr.message}")
+                                }
+                                fileDataList.add(bean)
+                                records++
+                            }
+                            if (totalCnt != records) {
+                                throw TransactionException(97, "商户[$shopacc]对账单下载[$billDate]文件总交易笔数和明细不符!")
+                            }
+                        }
                     }
+                    YnrccPayUtil.NO_RECORDS_TODAY -> {
+                        //fixme:当日无交易明细
+                    }
+                    else -> {
+                        //报错,退出对账单拉取
+                        logger.error("农商行网关快捷支付商户[$shopacc]对账单下载[$billDate]报错:${resp.message}")
+                        chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+                        chkfile.remark = "商户[$shopacc]请求获取对账文件报错:${resp.message}"
+                        transactionReconciliationService.saveOrUpdateTransactionChkfile(chkfile)
 
-                }
-                YnrccPayUtil.NO_RECORDS_TODAY -> {
-                    //当日无交易明细,也创建空记录
-                    transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "请求银行返回:当日无交易明细")
-                    //成功
-                    return successDownload("当日无交易明细", checkStatus.apply {
-                        this.chkfileUrl = "none"
-                    })
-                }
-                else -> {
-                    //报错,退出对账单拉取
-                    logger.error("农商行网关快捷支付对账单下载[$billDate]报错:${resp.message}")
-                    chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
-                    chkfile.remark = "请求获取对账文件报错:${resp.message}"
-                    transactionReconciliationService.saveOrUpdateTransactionChkfile(chkfile)
-
-                    //失败
-                    return AgentResponse<TSourceTypeCheckStatus>().also {
-                        it.code = AgentCode.FAIL
-                        it.agentMsg = chkfile.remark
-                        it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
-                            this.remark = chkfile.remark
-                        })
+                        //失败,直接返回
+                        return AgentResponse<TSourceTypeCheckStatus>().also {
+                            it.code = AgentCode.FAIL
+                            it.agentMsg = chkfile.remark
+                            it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
+                                this.remark = chkfile.remark
+                            })
+                        }
                     }
                 }
             }
+
+            if(fileDataList.isEmpty()){
+                //当日无交易明细,也创建空记录
+                transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "请求银行返回:当日无交易明细")
+                //成功
+                return successDownload("当日无交易明细", checkStatus.apply {
+                    this.chkfileUrl = "none"
+                })
+            }
+
+            //保存对账单
+            val failcnt = doBatchSaveYnrccPayCheckDetails(chkfile, fileDataList)
+            if (failcnt > 0) {
+                chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+                chkfile.remark = "对账单数据存在保存失败记录,请手动核对对账单数据"
+                transactionReconciliationService.saveOrUpdateTransactionChkfile(chkfile)
+
+                return AgentResponse<TSourceTypeCheckStatus>().also {
+                    it.code = AgentCode.FAIL
+                    it.agentMsg = chkfile.remark
+                    it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
+                        this.checkFileOk = false
+                        this.chkfileUrl = "none"
+                        this.remark = chkfile.remark
+                    })
+                }
+            }
+
+            //成功
+            transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "对账单数据下载成功")
+            return successDownload("[$billDate]日对账数据入库成功", checkStatus.apply {
+                this.chkfileUrl = "none"
+            })
+
         } catch (et: TransactionException) {
             return AgentResponse<TSourceTypeCheckStatus>().also {
                 it.code = AgentCode.FAIL
@@ -154,4 +201,26 @@
         }
     }
 
+    private fun doBatchSaveYnrccPayCheckDetails(chkfile: TTransactionChkfile, list: ArrayList<YnrccPayChkfile>): Int {
+        try {
+            transactionReconciliationService.doBatchSaveYnrccPayTransactionChkDtl(chkfile, list)
+            return 0  //批量保存成功,无失败
+        } catch (e1: Exception) {
+        }
+
+        //批量保存有异常,逐笔保存
+        var failcnt = 0
+        var index = 1
+        for (bean in list) {
+            try {
+                transactionReconciliationService.saveYnrccPayTransactionChkDtl(chkfile, bean, index++)
+            } catch (e2: Exception) {
+                failcnt++
+                e2.printStackTrace()
+                continue
+            }
+        }
+        return failcnt
+    }
+
 }
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt
index f01d870..fd77c55 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/async_tasks.kt
@@ -7,6 +7,7 @@
 import com.supwisdom.dlpay.api.domain.TShopdtl
 import com.supwisdom.dlpay.api.domain.TTransactionMain
 import com.supwisdom.dlpay.api.repositories.ShopaccService
+import com.supwisdom.dlpay.api.service.ConsumePayService
 import com.supwisdom.dlpay.api.service.DtlQueryResultService
 import com.supwisdom.dlpay.api.service.TransactionServiceProxy
 import com.supwisdom.dlpay.exception.TransactionException
@@ -123,6 +124,9 @@
     @Autowired
     private lateinit var systemUtilService: SystemUtilService
 
+    @Autowired
+    lateinit var consumePayService: ConsumePayService
+
     @Async("queryAgentPayResult")
     fun queryResult(transaction: TTransactionMain, qcnt: Int) {
         try {
@@ -148,31 +152,41 @@
                     transaction.sourceType + "Agent")
 
             logger.info("refno=[${transaction.refno}]开始第" + (qcnt + 1) + "次查询支付结果:")
+            var endFlag = false
             val resp = service.query(transaction)
             when (resp.code) {
                 AgentCode.SUCCESS -> {
                     //查询成功
                     when (resp.dtlStatus) {
-                        DtlStatus.SUCCESS ->
+                        DtlStatus.SUCCESS -> {
+                            endFlag = true
                             transactionService.success(transaction.refno, resp.agentRefno, false) //流水成功
+                        }
                         DtlStatus.REFUND -> {
                             //流水已退款 TODO:流水成功后才退款,无查询原成功流水逻辑
+                            endFlag = true
                             logger.error("refno=[${transaction.refno}]查询结果为:已退款!!!")
                             return
                         }
                         DtlStatus.PARTIAL_REFUND -> {
                             //流水已部分退款 TODO:暂无逻辑
+                            endFlag = true
                             logger.error("refno=[${transaction.refno}]查询结果为:已部分退款!!!")
                             return
                         }
                         else -> {
                             //流水失败
+                            endFlag = true
                             transactionService.fail(transaction.refno, "查询流水状态为交易失败")
                         }
                     }
                 }
-                AgentCode.REFNO_NOT_EXISTS ->
-                    transactionService.fail(transaction.refno, "银行流水不存在") //银行返回流水不存在
+                AgentCode.REFNO_NOT_EXISTS -> {
+                    if(TradeDict.REVERSE_FLAG_NONE == transaction.reverseType) {
+                        endFlag = true
+                        transactionService.fail(transaction.refno, "银行流水不存在") //银行返回流水不存在
+                    }
+                }
                 AgentCode.REQUIRE_QUERY -> {
                     queryResult(transaction, qcnt + 1)  //查询次数加1
                 }
@@ -181,6 +195,36 @@
                     logger.error("查询refno=[${transaction.refno}]流水结果返回失败:code=[${resp.agentCode}],message=[${resp.agentMsg}]")
                 }
             }
+
+            //fixme: 撤销或退款可能不需要传商户订单号,必须查询原流水状态
+            if (!endFlag && (TradeDict.REVERSE_FLAG_CANCEL == transaction.reverseType || TradeDict.REVERSE_FLAG_REFUND == transaction.reverseType)) {
+                consumePayService.getTransactionMainDtl(transaction.reverseRefno, null, null)?.let {
+                    val origResp = service.query(it) //冲正流水,只能查询原始流水是否为退款状态
+                    when (origResp.code) {
+                        AgentCode.SUCCESS ->{
+                            when (origResp.dtlStatus) {
+                                DtlStatus.SUCCESS ->
+                                    transactionService.fail(transaction.refno, "查询确认退款失败!") //原始流水还是成功状态,则退款流水必定失败!!!
+                                DtlStatus.REFUND -> {
+                                    //流水已退款
+                                    transactionService.success(transaction.refno, origResp.agentRefno, false) //TODO: 确保这条原始流水只有一笔全额冲正流水成功!!!
+                                }
+                                DtlStatus.PARTIAL_REFUND -> {
+                                    //流水已部分退款 TODO:暂无逻辑
+                                }
+                                else -> {
+                                    //流水失败 无逻辑
+                                }
+                            }
+                        }
+                        else -> {
+                            //其他都重新查
+                            queryResult(transaction, qcnt + 1)  //查询次数加1
+                        }
+                    }
+                }
+            }
+
         } catch (ex: Exception) {
             ex.printStackTrace()
         }
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
index c9a6945..8d8a26d 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/consume_api_controller.kt
@@ -11,12 +11,11 @@
 import com.supwisdom.dlpay.api.bean.groups.InitAction
 import com.supwisdom.dlpay.api.domain.TSourceType
 import com.supwisdom.dlpay.api.service.*
-import com.supwisdom.dlpay.api.util.Constants
 import com.supwisdom.dlpay.exception.TransactionCheckException
 import com.supwisdom.dlpay.framework.ResponseBodyBuilder
 import com.supwisdom.dlpay.framework.service.SystemUtilService
-import com.supwisdom.dlpay.framework.tenant.TenantContext
 import com.supwisdom.dlpay.framework.util.*
+import mu.KotlinLogging
 import org.apache.commons.lang3.StringUtils
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.context.ApplicationContext
@@ -62,6 +61,8 @@
     @Autowired
     private lateinit var ynrccNetPayBusinessService: YnrccNetPayBusinessService
 
+    private val logger = KotlinLogging.logger { }
+
     /**
      * ============================================================================
      * 消费流水结果查询统一接口
@@ -984,18 +985,91 @@
     }
 
     /**
-     * 直接调第三方查询流水结果 (方便测试)
+     * ============================================================================
+     *                      直接调第三方查询流水结果 (通用)
+     * ============================================================================
      * */
     @PostMapping("/thirdquery")
-    fun thirdToQueryResult(refno: String): ResponseEntity<Any> {
-        consumePayService.getTransactionMainDtl(refno, null, null)?.let {
-            agentQueryResultTask.queryResult(it, 0)
-            return ResponseEntity.ok(
-                ResponseBodyBuilder.create()
-                    .success("已成功进入查询异步查询队列")
-            )
-        } ?: return ResponseEntity.ok(
-            ResponseBodyBuilder.create()
+    fun queryDtlResultByThird(@Valid @RequestBody param: QueryDtlResultParam): ResponseEntity<Any> {
+        consumePayService.getTransactionMainDtl(param.refno, param.billno, param.shopaccno)?.let {
+            if (TradeDict.DTL_STATUS_SUCCESS == it.status || TradeDict.DTL_STATUS_FAIL == it.status) {
+                return ResponseEntity.ok(ResponseBodyBuilder.create()
+                    .success(QueryTransDtlResponse(it.refno, it.outTradeNo, it.shopDtl.amount,
+                        it.status, it.sourceType, it.personDtl.payinfo, it.reverseFlag,
+                        it.shopDtl.transdesc,it.personDtl.remark), "查询成功"))
+            }
+
+            //发往第三方查询
+            var endFlag = false
+            val service = ApplicationUtil.findAgentPayService<Any>(applicationContext, it.sourceType + "Agent")
+            val resp = service.query(it)
+            when (resp.code) {
+                AgentCode.SUCCESS -> {
+                    //查询成功
+                    when (resp.dtlStatus) {
+                        DtlStatus.SUCCESS -> {
+                            endFlag = true
+                            transactionService.success(it.refno, resp.agentRefno, false) //流水成功
+                        }
+                        DtlStatus.REFUND -> {
+                            //流水已退款 TODO:流水成功后才退款,无查询原成功流水逻辑
+                        }
+                        DtlStatus.PARTIAL_REFUND -> {
+                            //流水已部分退款 TODO:暂无逻辑
+                        }
+                        else -> {
+                            //流水失败
+                            endFlag = true
+                            transactionService.fail(it.refno, "查询流水状态为交易失败")
+                        }
+                    }
+                }
+                AgentCode.REFNO_NOT_EXISTS -> {
+                    if(TradeDict.REVERSE_FLAG_NONE == it.reverseType){
+                        endFlag = true
+                        transactionService.fail(it.refno, "银行流水不存在") //银行返回流水不存在
+                    }
+                }
+                AgentCode.REQUIRE_QUERY -> {
+                    //流水无结果 TODO: 还需继续查询
+                }
+                else -> {
+                    //其他明确错误,查询失败
+                    logger.error("向第三方查询 refno=[${it.refno}]流水结果返回失败:code=[${resp.agentCode}],message=[${resp.agentMsg}]")
+                }
+            }
+
+            //TODO: 撤销或退款可能不需要传商户订单号,必须查询原流水状态
+            if (!endFlag && (TradeDict.REVERSE_FLAG_CANCEL == it.reverseType || TradeDict.REVERSE_FLAG_REFUND == it.reverseType)) {
+                consumePayService.getTransactionMainDtl(it.reverseRefno, null, null)?.let { origDtl ->
+                    val origResp = service.query(origDtl) //有冲正流水,原流水必定是成功流水。判断原流水状态是否为退款状态
+                    when (origResp.code) {
+                        AgentCode.SUCCESS -> {
+                            when (origResp.dtlStatus) {
+                                DtlStatus.SUCCESS ->
+                                    transactionService.fail(it.refno, "查询确认退款失败!") //原始流水还是成功状态,则退款流水必定失败!!!
+                                DtlStatus.REFUND -> {
+                                    //流水已退款
+                                    transactionService.success(it.refno, origResp.agentRefno, false) //TODO: 确保这条原始流水只有一笔全额冲正流水成功!!!
+                                }
+                                DtlStatus.PARTIAL_REFUND -> {
+                                    //流水已部分退款 TODO:暂无逻辑
+                                }
+                                else -> {
+                                    //流水失败 无逻辑
+                                }
+                            }
+                        }
+                        else -> {
+                            logger.error("向第三方查询 refno=[${it.refno}]冲正流水结果,原始流水支付状态查询返回失败:code=[${origResp.agentCode}],message=[${origResp.agentMsg}]")
+                        }
+                    }
+                }
+            }
+
+            return queryDtlResult(param) //直接查询逻辑
+
+        } ?: return ResponseEntity.ok(ResponseBodyBuilder.create()
                 .fail(TradeErrorCode.TRANSACTION_NOT_EXISTS, "流水不存在")
         )
     }
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
index 9937a31..8292e63 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
@@ -1,6 +1,7 @@
 package com.supwisdom.dlpay.api.service.impl
 
 import com.supwisdom.dlpay.agent.citizencard.YnrccUtil
+import com.supwisdom.dlpay.agent.ynrccpay.YnrccPayChkfile
 import com.supwisdom.dlpay.api.bean.YnrccChkfileBean
 import com.supwisdom.dlpay.api.dao.PersondtlDao
 import com.supwisdom.dlpay.api.dao.TransactionChkdtlDao
@@ -9,6 +10,7 @@
 import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
 import com.supwisdom.dlpay.api.domain.TTransactionChkdtl
 import com.supwisdom.dlpay.api.domain.TTransactionChkfile
+import com.supwisdom.dlpay.api.domain.TTransactionMain
 import com.supwisdom.dlpay.api.service.SourceTypeService
 import com.supwisdom.dlpay.api.service.TransactionReconciliationService
 import com.supwisdom.dlpay.api.service.TransactionServiceProxy
@@ -305,4 +307,72 @@
         transactionChkfileDao.save(chkfile)
         return sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus)
     }
+
+    override fun doBatchSaveYnrccPayTransactionChkDtl(chkfile: TTransactionChkfile, list: ArrayList<YnrccPayChkfile>): Boolean {
+        var idx=1
+        for (bean in list) {
+            saveYnrccPayTransactionChkDtl(chkfile, bean, idx++)
+        }
+        return true
+    }
+
+    override fun saveYnrccPayTransactionChkDtl(chkfile: TTransactionChkfile, bean: YnrccPayChkfile, idx:Int): TTransactionChkdtl {
+        return when(bean.payFlag){
+            YnrccPayChkfile.PAY->{
+                transactionChkdtlDao.save(TTransactionChkdtl().apply {
+                    this.chkfileId = chkfile.id
+                    this.accdate = chkfile.accdate
+                    this.sourcetype = chkfile.sourcetype
+                    this.recordno = idx
+                    this.amount = -1 * bean.amount.toDouble()
+                    this.otherRefno = bean.agentRefno
+                    this.localRefno = bean.billNo
+                    this.otherAccdate = bean.clearDate
+                    this.transtype = "pay"
+                    this.otherStatus = "success"
+                    this.remark = null
+                    this.extdata = "农商行网关快捷支付"
+                    this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_UNCHECK
+                    this.resolved = ConstantUtil.CHKDTL_RESOLVED_NONE
+                    this.lastsaved = systemUtilService.sysdatetime.sysdate
+                    this.tenantid = TenantContext.getTenantSchema()
+                })
+            }
+            YnrccPayChkfile.REFUND->{
+                val refno = bean.agentRefno //商户订单号
+                var localRefno = bean.billNo
+                val refundSuccessList = transactionMainDao.findByRefundSuccessTransdtl(refno)
+                if (null != refundSuccessList && refundSuccessList.size > 0) {
+                    localRefno = refundSuccessList[0].refno //成功的退款流水
+                } else {
+                    val refundTransdtlList = transactionMainDao.findByRefundTransdtl(refno)
+                    if (null != refundTransdtlList && refundTransdtlList.size > 0) {
+                        localRefno = refundTransdtlList[0].refno //第一笔退款的流水
+                    }
+                }
+
+                transactionChkdtlDao.save(TTransactionChkdtl().apply {
+                    this.chkfileId = chkfile.id
+                    this.accdate = chkfile.accdate
+                    this.sourcetype = chkfile.sourcetype
+                    this.recordno = idx
+                    this.amount = bean.amount.toDouble()
+                    this.otherRefno = bean.billNo
+                    this.localRefno = localRefno
+                    this.otherAccdate = bean.clearDate
+                    this.transtype = "refund"
+                    this.otherStatus = "success"
+                    this.remark = null
+                    this.extdata = "农商行网关快捷支付退款[${bean.agentRefno}]"
+                    this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_UNCHECK
+                    this.resolved = ConstantUtil.CHKDTL_RESOLVED_NONE
+                    this.lastsaved = systemUtilService.sysdatetime.sysdate
+                    this.tenantid = TenantContext.getTenantSchema()
+                })
+            }
+            else -> {
+                throw TransactionException(97, "不能识别对账单明细中交易代码[${bean.payFlag}]!")
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt
index d7ce27d..5c4e132 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_reconciliation_service.kt
@@ -1,5 +1,6 @@
 package com.supwisdom.dlpay.api.service
 
+import com.supwisdom.dlpay.agent.ynrccpay.YnrccPayChkfile
 import com.supwisdom.dlpay.api.bean.YnrccChkfileBean
 import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
 import com.supwisdom.dlpay.api.domain.TTransactionChkdtl
@@ -65,6 +66,10 @@
     @Transactional(rollbackFor = [Exception::class])
     fun doConfirmChkfileStatus(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TSourceTypeCheckStatus
 
+    @Transactional(rollbackFor = [Exception::class])
+    fun doBatchSaveYnrccPayTransactionChkDtl(chkfile: TTransactionChkfile, list: ArrayList<YnrccPayChkfile>): Boolean
 
+    @Transactional(rollbackFor = [Exception::class])
+    fun saveYnrccPayTransactionChkDtl(chkfile: TTransactionChkfile, bean: YnrccPayChkfile, idx: Int): TTransactionChkdtl
 
 }
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/framework_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/framework_controller.kt
index a55845f..c5e603c 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/framework_controller.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/framework/controller/framework_controller.kt
@@ -1,17 +1,24 @@
 package com.supwisdom.dlpay.framework.controller
 
+import com.google.gson.Gson
 import com.jcabi.manifests.Manifests
+import com.supwisdom.dlpay.agent.service.YnrccNetPayService
 import com.supwisdom.dlpay.api.bean.ApiVersionResponse
 import com.supwisdom.dlpay.framework.ResponseBodyBuilder
+import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.http.ResponseEntity
 import org.springframework.web.bind.annotation.GetMapping
 import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RequestParam
 import org.springframework.web.bind.annotation.RestController
 
 
 @RestController
 @RequestMapping("/api/common")
 class AboutController {
+    @Autowired
+    lateinit var ynrccNetPayService: YnrccNetPayService
+
     @GetMapping("/version")
     fun version(): ResponseEntity<Any> {
         return try {
@@ -24,4 +31,27 @@
                     .success(ApiVersionResponse("unknown")))
         }
     }
+
+    /**
+     * 查看农商行快捷支付对账单
+     * */
+    @GetMapping("/ynrccpaychkfile")
+    fun testYnrccpayChkfile(
+        @RequestParam("accdate") accdate: String,
+        @RequestParam("shopaccno") shopaccno: String
+    ): ResponseEntity<Any> {
+        return try {
+            val resp = ynrccNetPayService.doQueryYnrccPayChkfile(accdate, shopaccno)
+
+            ResponseEntity.ok(
+                ResponseBodyBuilder.create()
+                    .success(Gson().toJson(resp))
+            )
+        } catch (e: IllegalArgumentException) {
+            ResponseEntity.ok(
+                ResponseBodyBuilder.create()
+                    .fail(99, "throw exception!")
+            )
+        }
+    }
 }
\ No newline at end of file