改进消费接口,支持了退款业务
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/annotation/TransDate.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/annotation/TransDate.java
new file mode 100644
index 0000000..612d6f2
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/annotation/TransDate.java
@@ -0,0 +1,22 @@
+package com.supwisdom.dlpay.api.annotation;
+
+
+import com.supwisdom.dlpay.api.validator.TransDateValidator;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+@Constraint(validatedBy = {TransDateValidator.class})
+@Documented
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TransDate {
+  String message() default "交易日期格式错误";
+
+  Class<?>[] groups() default {};
+
+  Class<? extends Payload>[] payload() default {};
+
+  String value() default "";
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/annotation/TransTime.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/annotation/TransTime.java
new file mode 100644
index 0000000..ddb74c2
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/annotation/TransTime.java
@@ -0,0 +1,22 @@
+package com.supwisdom.dlpay.api.annotation;
+
+
+import com.supwisdom.dlpay.api.validator.TransTimeValidator;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+@Constraint(validatedBy = {TransTimeValidator.class})
+@Documented
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TransTime {
+  String message() default "交易时间格式错误";
+
+  Class<?>[] groups() default {};
+
+  Class<? extends Payload>[] payload() default {};
+
+  String value() default "";
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizParam.java
index dd83c93..8c43519 100644
--- a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizParam.java
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizParam.java
@@ -2,6 +2,8 @@
 
 import com.sun.java.swing.action.CancelAction;
 import com.supwisdom.dlpay.api.APIRequestParam;
+import com.supwisdom.dlpay.api.annotation.TransDate;
+import com.supwisdom.dlpay.api.annotation.TransTime;
 import com.supwisdom.dlpay.api.bean.groups.ConfirmAction;
 import com.supwisdom.dlpay.api.bean.groups.InitAction;
 import com.supwisdom.dlpay.api.bean.groups.QueryAction;
@@ -15,10 +17,10 @@
 
   private @NotNull(message = "操作员流水号不能为空", groups = {InitAction.class, ConfirmAction.class, QueryAction.class}) String operSeqno;
 
-  @NotNull(groups = {InitAction.class})
+  @TransDate(groups = {InitAction.class})
   private String transdate;
 
-  @NotNull(groups = {InitAction.class})
+  @TransTime(groups = {InitAction.class})
   private String transtime;
 
   private String userid;
@@ -43,7 +45,7 @@
   @NotNull(message = "交易摘要不能为空", groups = {InitAction.class})
   private String summary;
 
-  @NotNull(message = "记账日期不能为空", groups = {ConfirmAction.class, CancelAction.class, QueryAction.class})
+  @TransDate(message = "记账日期不能为空", groups = {ConfirmAction.class, CancelAction.class, QueryAction.class})
   private String accdate;
 
   @NotNull(message = "交易参考号不能为空", groups = {ConfirmAction.class, CancelAction.class, QueryAction.class})
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizRefundParam.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizRefundParam.java
new file mode 100644
index 0000000..70f4656
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizRefundParam.java
@@ -0,0 +1,83 @@
+package com.supwisdom.dlpay.api.bean;
+
+import com.supwisdom.dlpay.api.APIRequestParam;
+import com.supwisdom.dlpay.api.annotation.TransDate;
+import com.supwisdom.dlpay.api.annotation.TransTime;
+import com.supwisdom.dlpay.api.bean.groups.ConfirmAction;
+import com.supwisdom.dlpay.api.bean.groups.InitAction;
+import com.supwisdom.dlpay.api.exception.RequestParamCheckException;
+
+import javax.validation.constraints.NotNull;
+
+public class CardBizRefundParam extends APIRequestParam {
+  @NotNull(message = "操作员号不能空", groups = {InitAction.class})
+  private String operid;
+
+  @NotNull(message = "操作员流水号不能为空", groups = {InitAction.class})
+  private String operSeqno;
+
+  @NotNull(message = "原始流水参考号不能为空", groups = {InitAction.class, ConfirmAction.class})
+  private String originRefno;
+
+  @NotNull(message = "退款总金额不能为空", groups = {InitAction.class})
+  private Integer totalAmount;
+
+  @TransDate(groups = {InitAction.class})
+  private String transdate;
+
+  @TransTime(groups = {InitAction.class})
+  private String transtime;
+
+  public String getOperid() {
+    return operid;
+  }
+
+  public void setOperid(String operid) {
+    this.operid = operid;
+  }
+
+  public String getOperSeqno() {
+    return operSeqno;
+  }
+
+  public void setOperSeqno(String operSeqno) {
+    this.operSeqno = operSeqno;
+  }
+
+  public String getOriginRefno() {
+    return originRefno;
+  }
+
+  public void setOriginRefno(String originRefno) {
+    this.originRefno = originRefno;
+  }
+
+  public Integer getTotalAmount() {
+    return totalAmount;
+  }
+
+  public void setTotalAmount(Integer totalAmount) {
+    this.totalAmount = totalAmount;
+  }
+
+  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;
+  }
+
+  @Override
+  public boolean checkParam() throws RequestParamCheckException {
+    return true;
+  }
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizResponse.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizResponse.java
index c54a3ee..704c809 100644
--- a/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizResponse.java
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/bean/CardBizResponse.java
@@ -14,6 +14,8 @@
 
   private Integer accountBal;
 
+  private String transStatus;
+
   public String getRefno() {
     return refno;
   }
@@ -46,4 +48,12 @@
   public void setAccountBal(Integer accountBal) {
     this.accountBal = accountBal;
   }
+
+  public String getTransStatus() {
+    return transStatus;
+  }
+
+  public void setTransStatus(String transStatus) {
+    this.transStatus = transStatus;
+  }
 }
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/validator/TransDateValidator.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/validator/TransDateValidator.java
new file mode 100644
index 0000000..cb5b243
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/validator/TransDateValidator.java
@@ -0,0 +1,30 @@
+package com.supwisdom.dlpay.api.validator;
+
+import com.supwisdom.dlpay.api.annotation.TransDate;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+public class TransDateValidator implements ConstraintValidator<TransDate, String> {
+
+  @Override
+  public boolean isValid(String value, ConstraintValidatorContext context) {
+    if (value == null || value.isEmpty()) {
+      return false;
+    }
+    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+    try {
+      sdf.parse(value);
+      return true;
+    } catch (ParseException e) {
+      e.printStackTrace();
+    }
+    return false;
+  }
+
+  @Override
+  public void initialize(TransDate constraintAnnotation) {
+  }
+}
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/api/validator/TransTimeValidator.java b/payapi-common/src/main/java/com/supwisdom/dlpay/api/validator/TransTimeValidator.java
new file mode 100644
index 0000000..96ac618
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/api/validator/TransTimeValidator.java
@@ -0,0 +1,31 @@
+package com.supwisdom.dlpay.api.validator;
+
+import com.supwisdom.dlpay.api.annotation.TransTime;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+public class TransTimeValidator implements ConstraintValidator<TransTime, String> {
+
+  @Override
+  public boolean isValid(String value, ConstraintValidatorContext context) {
+    if (value == null || value.isEmpty()) {
+      return false;
+    }
+    SimpleDateFormat sdf = new SimpleDateFormat("HHmmss");
+
+    try {
+      sdf.parse(value);
+      return true;
+    } catch (ParseException e) {
+      e.printStackTrace();
+    }
+    return false;
+  }
+
+  @Override
+  public void initialize(TransTime constraintAnnotation) {
+  }
+}
diff --git a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/UserProxy.java b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/UserProxy.java
index 1b1bcd7..e761f70 100644
--- a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/UserProxy.java
+++ b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/proxy/UserProxy.java
@@ -31,9 +31,12 @@
   @PostMapping("/api/user/biz_confirm")
   CardBizResponse bizConfirm(@RequestBody CardBizParam param);
 
+  @PostMapping("/api/user/biz_refund_init")
+  CardBizResponse bizRefundInit(@RequestBody CardBizRefundParam param);
+
   @PostMapping("/api/user/biz_refund")
-  CardBizResponse bizRefund(@RequestBody CardBizParam param);
+  CardBizResponse bizRefund(String refno);
 
   @PostMapping("/api/user/biz_query")
-  CardBizResponse bizQuery(@RequestBody CardBizParam param);
+  CardBizResponse bizQuery(String refno);
 }
diff --git a/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java b/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
index cad5c82..1916f7a 100644
--- a/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
+++ b/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
@@ -38,6 +38,8 @@
   private final static String appid = "700001";
   private final static String secret = "d6dd7f0d4551419d8d11736d0f28df0d";
 
+  private final static String operid = "1001";
+
   @Autowired
   private ApiLoginProxy apiLoginProxy;
 
@@ -235,7 +237,7 @@
     CardBizParam param = new CardBizParam();
     param.setTransdate(DateUtil.getNow("yyyyMMdd"));
     param.setTranstime(DateUtil.getNow("HHmmss"));
-    param.setOperid("1001");
+    param.setOperid(operid);
     param.setOperSeqno(param.getTransdate() + param.getTranstime());
     param.setTotalAmount(10000);
     param.setCapitalSubjno("112201");
@@ -265,6 +267,26 @@
         + ", account " + response.getAccountBal() / 100.0
         + ", description " + response.getDescription() + "  summary " + response.getRetmsg());
 
+
+    CardBizRefundParam refundParam = new CardBizRefundParam();
+    refundParam.setOperid(operid);
+    refundParam.setTransdate(DateUtil.getNow("yyyyMMdd"));
+    refundParam.setTranstime(DateUtil.getNow("HHmmss"));
+    refundParam.setOperSeqno(refundParam.getTransdate() + refundParam.getTranstime() + "9");
+    refundParam.setTotalAmount(10000);
+    refundParam.setOriginRefno(response.getRefno());
+    response = userProxy.bizRefundInit(refundParam);
+    assertThat("user card biz refund init " + response.getRetcode() + response.getRetmsg() + response.getException(),
+        response.getRetcode(), equalTo(0));
+
+    System.out.println("biz refund init accdate " + response.getAccdate() + " , refno " + response.getRefno()
+        + ", description " + response.getDescription() + "  summary " + response.getRetmsg());
+
+    response = userProxy.bizRefund(response.getRefno());
+
+    System.out.println("biz refund accdate " + response.getAccdate() + " , refno " + response.getRefno()
+        + ", account " + response.getAccountBal() / 100.0
+        + ", description " + response.getDescription() + "  summary " + response.getRetmsg());
   }
 
   @Test
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/advices.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/advices.kt
index 1fda9f0..19f5064 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/advices.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/advices.kt
@@ -115,9 +115,9 @@
 
     @Around("restcontroller() && postmapping() && args(@RequestBody body, ..)")
     @Throws(Throwable::class)
-    fun logPostMethods(joinPoint: ProceedingJoinPoint, body: Any): Any {
+    fun logPostMethods(joinPoint: ProceedingJoinPoint, body: Any?): Any {
         return try {
-            if (body is APIRequestParam) {
+            if (body != null && body is APIRequestParam) {
                 //TenantContext.setTenantSchema(body.tenantid)
                 body.checkParam()
 
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt
index 432a84f..bc8b341 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/user_api_controller.kt
@@ -6,8 +6,10 @@
 import com.supwisdom.dlpay.api.bean.groups.InitAction
 import com.supwisdom.dlpay.api.exception.RequestParamCheckException
 import com.supwisdom.dlpay.api.service.*
+import com.supwisdom.dlpay.exception.TransactionCheckException
 import com.supwisdom.dlpay.exception.TransactionException
 import com.supwisdom.dlpay.framework.ResponseBodyBuilder
+import com.supwisdom.dlpay.framework.tenant.TenantContext
 import com.supwisdom.dlpay.framework.util.Subject
 import com.supwisdom.dlpay.framework.util.TradeCode
 import com.supwisdom.dlpay.framework.util.TradeDict
@@ -279,6 +281,7 @@
             accdate = transaction.accdate
             refno = transaction.refno
             description = builder.description
+            transStatus = transaction.status
         }
         return ResponseBodyBuilder.successEntity(result, "${request.summary}初始化成功")
     }
@@ -291,17 +294,81 @@
             refno = transaction.refno
             description = transaction.personDtl.transdesc
             accountBal = (transaction.personDtl.aftbal * 100).roundToInt()
+            transStatus = transaction.status
         }
         return ResponseBodyBuilder.successEntity(response, "${request.summary}确认成功")
     }
 
+    @PostMapping("/biz_refund_init")
+    fun userBizRefundInit(@RequestBody @Validated(InitAction::class) request: CardBizRefundParam): ResponseEntity<Any> {
+        val result = CardBizResponse()
+        val originTrans = transactionService.findTransactionByRefno(request.originRefno)
+                ?: return ResponseBodyBuilder.failEntity(result, TradeErrorCode.TRANSACTION_NOT_EXISTS,
+                        "退款原交易参考号不存在")
+        if (originTrans.tenantid != TenantContext.getTenantSchema()) {
+            return ResponseBodyBuilder.failEntity(result, TradeErrorCode.INPUT_DATA_ERROR,
+                    "退款交易参考号租户错误")
+        }
+
+        if (!originTrans.person) {
+            return ResponseBodyBuilder.failEntity(result, TradeErrorCode.INPUT_DATA_ERROR,
+                    "指定交易流水没有个人交易明细,无法退款")
+        }
+
+        val builder = TransactionBuilder().apply {
+            this.setTransInfo(request.transdate, request.transtime, TradeCode.TRANSCODE_CARD_BIZ,
+                    "balance")
+            this.setOutTransInfo(request.operid, request.operSeqno)
+        }
+        val transaction = builder.refundInit(request.originRefno, request.totalAmount / 100.0,
+                transactionService)
+
+        result.apply {
+            refno = transaction.refno
+            accdate = transaction.accdate
+            transStatus = transaction.status
+            if (transaction.person) {
+                description = transaction.personDtl.transdesc
+            }
+        }
+        return ResponseBodyBuilder.successEntity(result, "初始化成功")
+    }
+
     @PostMapping("/biz_refund")
-    fun userBizRefund(@RequestBody @Valid request: CardBizParam): ResponseEntity<Any> {
-        return ResponseEntity.ok(ResponseBodyBuilder.create().success())
+    fun userBizRefund(@RequestBody refno: String): ResponseEntity<Any> {
+        val result = CardBizResponse()
+        val transaction = transactionService.success(refno)
+        result.apply {
+            this.refno = transaction.refno
+            accdate = transaction.accdate
+            transStatus = transaction.status
+            if (transaction.person) {
+                description = transaction.personDtl.transdesc
+                accountBal = (transaction.personDtl.aftbal * 100.0).roundToInt()
+            }
+        }
+        return ResponseBodyBuilder.failEntity(result, TradeErrorCode.INPUT_DATA_ERROR, "未实现")
     }
 
     @GetMapping("/biz_query")
-    fun userBizQuery(@RequestBody @Valid request: CardBizParam): ResponseEntity<Any> {
-        return ResponseEntity.ok(ResponseBodyBuilder.create().success())
+    fun userBizQuery(@RequestBody refno: String): ResponseEntity<Any> {
+        val transaction = transactionService.findTransactionByRefno(refno)
+                ?: throw TransactionCheckException(TradeErrorCode.TRANSACTION_NOT_EXISTS
+                        , "交易不存在")
+        val result = CardBizResponse()
+        if (transaction.tenantid != TenantContext.getTenantSchema()) {
+            return ResponseBodyBuilder.failEntity(result, TradeErrorCode.INPUT_DATA_ERROR,
+                    "非本租户交易参考号")
+        }
+        result.apply {
+            this.refno = transaction.refno
+            accdate = transaction.accdate
+            if (transaction.person) {
+                description = transaction.personDtl.transdesc
+                accountBal = (transaction.personDtl.aftbal * 100.0).roundToInt()
+            }
+            transStatus = transaction.status
+        }
+        return ResponseBodyBuilder.successEntity(result)
     }
 }
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt
index 6fe6933..e7589f1 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/transaction_service.kt
@@ -2,6 +2,7 @@
 
 import com.supwisdom.dlpay.api.ShopAccBalanceAsyncTask
 import com.supwisdom.dlpay.api.TransactionBuilder
+import com.supwisdom.dlpay.api.dao.TransactionMainDao
 import com.supwisdom.dlpay.api.domain.TTransactionMain
 import com.supwisdom.dlpay.exception.TransactionProcessException
 import com.supwisdom.dlpay.framework.util.TradeErrorCode
@@ -69,6 +70,9 @@
     @Autowired
     private lateinit var kafkaSendMsgService: KafkaSendMsgService
 
+    @Autowired
+    private lateinit var transactionMainDao: TransactionMainDao
+
 
     fun init(builder: TransactionBuilder): TTransactionMain {
         try {
@@ -143,4 +147,7 @@
         }
     }
 
+    fun findTransactionByRefno(refno: String): TTransactionMain? {
+        return transactionMainDao.findByRefno(refno)
+    }
 }