增加 qrcode 聚合付流程框
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodePayParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodePayParam.java
new file mode 100644
index 0000000..d7eecb2
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodePayParam.java
@@ -0,0 +1,90 @@
+package com.supwisdom.dlpay.api.bean;
+
+import com.supwisdom.dlpay.api.APIRequestParam;
+import com.supwisdom.dlpay.api.annotation.Sign;
+import com.supwisdom.dlpay.api.exception.RequestParamCheckException;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+public class QrcodePayParam extends APIRequestParam {
+  @Sign
+  private String billno;
+  @Sign
+  private String refno;
+  @Sign
+  @NotEmpty(message = "支付码不能为空")
+  private String qrcoode;
+  @Sign
+  @NotEmpty(message = "交易日期不能为空")
+  private String transdate;
+  @Sign
+  @NotEmpty(message = "交易时间不能为空")
+  private String transtime;
+  @Sign
+  @NotNull(message = "交易金额不能为空")
+  private Integer amount;
+  @Sign
+  private String shopaccno;
+
+  public String getBillno() {
+    return billno;
+  }
+
+  public void setBillno(String billno) {
+    this.billno = billno;
+  }
+
+  public String getRefno() {
+    return refno;
+  }
+
+  public void setRefno(String refno) {
+    this.refno = refno;
+  }
+
+  public String getQrcoode() {
+    return qrcoode;
+  }
+
+  public void setQrcoode(String qrcoode) {
+    this.qrcoode = qrcoode;
+  }
+
+  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 Integer getAmount() {
+    return amount;
+  }
+
+  public void setAmount(Integer amount) {
+    this.amount = amount;
+  }
+
+  public String getShopaccno() {
+    return shopaccno;
+  }
+
+  public void setShopaccno(String shopaccno) {
+    this.shopaccno = shopaccno;
+  }
+
+  @Override
+  public boolean checkParam() throws RequestParamCheckException {
+    return true;
+  }
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodePayResponse.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodePayResponse.java
new file mode 100644
index 0000000..b6aae8f
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/QrcodePayResponse.java
@@ -0,0 +1,40 @@
+package com.supwisdom.dlpay.api.bean;
+
+public class QrcodePayResponse extends ApiResponse {
+  private String refno;
+  private String userid;
+  private Boolean anonymous;
+  private Integer amount;
+
+  public String getRefno() {
+    return refno;
+  }
+
+  public void setRefno(String refno) {
+    this.refno = refno;
+  }
+
+  public String getUserid() {
+    return userid;
+  }
+
+  public void setUserid(String userid) {
+    this.userid = userid;
+  }
+
+  public Boolean getAnonymous() {
+    return anonymous;
+  }
+
+  public void setAnonymous(Boolean anonymous) {
+    this.anonymous = anonymous;
+  }
+
+  public Integer getAmount() {
+    return amount;
+  }
+
+  public void setAmount(Integer amount) {
+    this.amount = amount;
+  }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/dao/QrcodePatternDao.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/dao/QrcodePatternDao.java
new file mode 100644
index 0000000..54da64e
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/dao/QrcodePatternDao.java
@@ -0,0 +1,7 @@
+package com.supwisdom.dlpay.agent.dao;
+
+import com.supwisdom.dlpay.agent.domain.QrcodePattern;
+import org.springframework.data.repository.CrudRepository;
+
+public interface QrcodePatternDao extends CrudRepository<QrcodePattern, Integer> {
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/dao/QrcodePayTransDao.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/dao/QrcodePayTransDao.java
new file mode 100644
index 0000000..1db85a8
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/dao/QrcodePayTransDao.java
@@ -0,0 +1,13 @@
+package com.supwisdom.dlpay.agent.dao;
+
+import com.supwisdom.dlpay.agent.domain.QrcodePayTrans;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+
+@Repository
+public interface QrcodePayTransDao extends CrudRepository<QrcodePayTrans, String> {
+  QrcodePayTrans findByRefnoAndTenantid(String refno, String tenantid);
+
+  void deleteByRefnoAndTenantid(String refno, String tenantid);
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/domain/QrcodePattern.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/domain/QrcodePattern.java
new file mode 100644
index 0000000..2949c61
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/domain/QrcodePattern.java
@@ -0,0 +1,46 @@
+package com.supwisdom.dlpay.agent.domain;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+
+@Table(name = "tb_qrcode_pattern")
+@Entity
+public class QrcodePattern {
+  @SequenceGenerator(name = "qrcode_pattern_id", sequenceName = "SEQ_QRCODE_PATTERN", allocationSize = 1, initialValue = 10)
+  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "qrcode_pattern_id")
+  @Id
+  @Column(name = "id")
+  private Integer id;
+
+  @Column(name = "sourcetype", length = 20)
+  @NotNull
+  private String sourceType;
+
+  @Column(name = "pattern", length = 300)
+  @NotNull
+  private String pattern;
+
+  public Integer getId() {
+    return id;
+  }
+
+  public void setId(Integer id) {
+    this.id = id;
+  }
+
+  public String getSourceType() {
+    return sourceType;
+  }
+
+  public void setSourceType(String sourceType) {
+    this.sourceType = sourceType;
+  }
+
+  public String getPattern() {
+    return pattern;
+  }
+
+  public void setPattern(String pattern) {
+    this.pattern = pattern;
+  }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/domain/QrcodePayTrans.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/domain/QrcodePayTrans.java
new file mode 100644
index 0000000..e5ccb69
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/domain/QrcodePayTrans.java
@@ -0,0 +1,77 @@
+package com.supwisdom.dlpay.agent.domain;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.sql.Timestamp;
+
+@Entity
+@Table(name = "tb_qrcode_pay_trans",
+    indexes = {@Index(name = "qrcode_pay_trans_idx", columnList = "qrcode, sourcetype"),
+        @Index(name = "qrcode_pay_trans_idx2", columnList = "tenantid")})
+public class QrcodePayTrans {
+  @Id
+  @NotNull
+  private String refno;
+  @Column(name = "agent_user_id", length = 200)
+  private String agentUserId;
+  @Column(name = "qrcode", length = 256)
+  @NotNull
+  private String qrcode;
+  @Column(name = "sourcetype", length = 30)
+  @NotNull
+  private String sourceType;
+  @NotNull
+  @Column(name = "create_time")
+  private Timestamp create_time;
+  @NotNull
+  @Column(name = "tenantid", length = 20)
+  private String tenantid;
+
+  public String getRefno() {
+    return refno;
+  }
+
+  public void setRefno(String refno) {
+    this.refno = refno;
+  }
+
+  public String getAgentUserId() {
+    return agentUserId;
+  }
+
+  public void setAgentUserId(String agentUserId) {
+    this.agentUserId = agentUserId;
+  }
+
+  public String getQrcode() {
+    return qrcode;
+  }
+
+  public void setQrcode(String qrcode) {
+    this.qrcode = qrcode;
+  }
+
+  public Timestamp getCreate_time() {
+    return create_time;
+  }
+
+  public void setCreate_time(Timestamp create_time) {
+    this.create_time = create_time;
+  }
+
+  public String getTenantid() {
+    return tenantid;
+  }
+
+  public void setTenantid(String tenantid) {
+    this.tenantid = tenantid;
+  }
+
+  public String getSourceType() {
+    return sourceType;
+  }
+
+  public void setSourceType(String sourceType) {
+    this.sourceType = sourceType;
+  }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/service/AgentServiceProxy.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/service/AgentServiceProxy.java
new file mode 100644
index 0000000..d3fb4f4
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/service/AgentServiceProxy.java
@@ -0,0 +1,65 @@
+package com.supwisdom.dlpay.agent.service;
+
+import com.supwisdom.dlpay.agent.dao.QrcodePayTransDao;
+import com.supwisdom.dlpay.agent.domain.QrcodePattern;
+import com.supwisdom.dlpay.agent.domain.QrcodePayTrans;
+import com.supwisdom.dlpay.framework.tenant.TenantContext;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+@Service
+public class AgentServiceProxy {
+  private final QrcodePayTransDao alipayTransDao;
+  private final QrcodePatternService qrcodePatternService;
+
+  public AgentServiceProxy(QrcodePayTransDao alipayTransDao,
+                           QrcodePatternService qrcodePatternService) {
+    this.alipayTransDao = alipayTransDao;
+    this.qrcodePatternService = qrcodePatternService;
+  }
+
+  public QrcodePayTrans qrcodePayTransFindByRefno(String refno) {
+    return alipayTransDao.findByRefnoAndTenantid(refno, TenantContext.getTenantSchema());
+  }
+
+  public QrcodePayTrans qrcodePayTransSaveOrUpdate(QrcodePayTrans bean) {
+    if (bean.getTenantid().isEmpty()) {
+      bean.setTenantid(TenantContext.getTenantSchema());
+    }
+    return alipayTransDao.save(bean);
+  }
+
+  public void qrcodePayTransDelete(String refno) {
+    alipayTransDao.deleteByRefnoAndTenantid(refno, TenantContext.getTenantSchema());
+  }
+
+  public QrcodePattern qrcodeMatch(String code) {
+    List<QrcodePattern> pattern = qrcodePatternService.getAllQrCodePattern();
+    List<QrcodePattern> found = new ArrayList<>();
+    for (QrcodePattern item : pattern) {
+      if (Pattern.matches(item.getPattern(), code)) {
+        found.add(item);
+      }
+    }
+    if (found.size() == 0) {
+      throw new IllegalArgumentException("不支持的Qrcode");
+    }
+    if (found.size() > 1) {
+      Set<String> foundST = new HashSet<>();
+      for (QrcodePattern item : found) {
+        if (!foundST.contains(item.getSourceType())) {
+          foundST.add(item.getSourceType());
+        }
+      }
+      if (foundST.size() > 1) {
+        throw new IllegalArgumentException("Qrcode匹配错误,找到多个sourcetype");
+      }
+    }
+    return found.get(0);
+  }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/service/QrcodePatternService.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/service/QrcodePatternService.java
new file mode 100644
index 0000000..d3a0397
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/service/QrcodePatternService.java
@@ -0,0 +1,9 @@
+package com.supwisdom.dlpay.agent.service;
+
+import com.supwisdom.dlpay.agent.domain.QrcodePattern;
+
+import java.util.List;
+
+public interface QrcodePatternService {
+  List<QrcodePattern> getAllQrCodePattern();
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/service/impl/QrcodePatternServiceImpl.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/service/impl/QrcodePatternServiceImpl.java
new file mode 100644
index 0000000..d2ad391
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/service/impl/QrcodePatternServiceImpl.java
@@ -0,0 +1,25 @@
+package com.supwisdom.dlpay.agent.service.impl;
+
+import com.supwisdom.dlpay.agent.dao.QrcodePatternDao;
+import com.supwisdom.dlpay.agent.domain.QrcodePattern;
+import com.supwisdom.dlpay.agent.service.QrcodePatternService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class QrcodePatternServiceImpl implements QrcodePatternService {
+  @Autowired
+  private QrcodePatternDao qrcodePatternDao;
+
+  @Override
+  @Cacheable(cacheNames = "qrcode_pattern_cache")
+  public List<QrcodePattern> getAllQrCodePattern() {
+    List<QrcodePattern> list = new ArrayList<>();
+    qrcodePatternDao.findAll().forEach(list::add);
+    return list;
+  }
+}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/alipay_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/alipay_service.kt
index eeecae9..e32f7d8 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/alipay_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/alipay_service.kt
@@ -7,19 +7,19 @@
 
 @Component("alipayAgent")
 class AlipayAgentService : AgentPayService {
-    override fun pay(transaction: TTransactionMain?): AgentResponse {
+    override fun pay(transaction: TTransactionMain): AgentResponse {
         TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
     }
 
-    override fun cancel(transaction: TTransactionMain?): AgentResponse {
+    override fun cancel(transaction: TTransactionMain): AgentResponse {
         TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
     }
 
-    override fun refund(transaction: TTransactionMain?): AgentResponse {
+    override fun refund(transaction: TTransactionMain): AgentResponse {
         TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
     }
 
-    override fun query(transaction: TTransactionMain?): AgentResponse {
+    override fun query(transaction: TTransactionMain): AgentResponse {
         TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
     }
 }
\ No newline at end of file
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 1f32d89..a8b6e05 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
@@ -434,6 +434,22 @@
                 .fail(TradeErrorCode.BUSINESS_DEAL_ERROR, "交易扣费失败"))
     }
 
+    @PostMapping("/qrcodepay/init")
+    fun qrcodePayInit(@Valid @RequestBody param: QrcodePayParam): ResponseEntity<Any> {
+        //1. 识别qrcode
+        //2. 初始化交易流水
+        //3. 调用第三方检查身份信息
+        TODO("")
+    }
+
+    @PostMapping("/qrcodepay/confirm")
+    fun qrcodePayConfirm(@Valid @RequestBody param: QrcodePayParam): ResponseEntity<Any> {
+        //1. 检查交易流水状态,并 wip
+        //2. 请求第三方交易
+        //3. 根据第三方返回处理业务流程
+        TODO("")
+    }
+
 // ============================================== //
 //
 //    @GetMapping("/account/payinit")
diff --git a/ynrcc-agent/src/test/java/com/supwisdom/agent/api/controller/YnrccApiControllerTest.java b/ynrcc-agent/src/test/java/com/supwisdom/agent/api/controller/YnrccApiControllerTest.java
new file mode 100644
index 0000000..25746e0
--- /dev/null
+++ b/ynrcc-agent/src/test/java/com/supwisdom/agent/api/controller/YnrccApiControllerTest.java
@@ -0,0 +1,7 @@
+package com.supwisdom.agent.api.controller;
+
+import static org.junit.Assert.*;
+
+public class YnrccApiControllerTest {
+
+}
\ No newline at end of file
diff --git a/ynrcc-agent/src/test/resources/bank_20190718.txt b/ynrcc-agent/src/test/resources/bank_20190718.txt
new file mode 100644
index 0000000..1831d7a
--- /dev/null
+++ b/ynrcc-agent/src/test/resources/bank_20190718.txt
@@ -0,0 +1,4 @@
+3|600|2|300|1|300|
+20190701|132234|BC5512|20190614135011000011|20190701132234BK00001|6223900000000000001|6321980000000000002|20190701|100|市民卡代扣消费|
+20190701|134434|BC5512|20190614135011000012|20190701132234BK00002|6223900000000000001|6321980000000000002|20190702|200|市民卡代扣消费|
+20190701|142234|BC5513|20190614135011000013|20190701132234BK00003|6223900000000000001|6321980000000000002|20190701|300|退款|
\ No newline at end of file