feat: 增加了 openapi spec
diff --git a/payapi/build.gradle b/payapi/build.gradle
index 3bbc883..656a319 100644
--- a/payapi/build.gradle
+++ b/payapi/build.gradle
@@ -18,10 +18,6 @@
 bootJar {
     enabled = true
     mainClassName = payapiStartClass
-//    def standalone = ""
-//    if (rootProject.hasProperty("no-multi-tenant")) {
-//        standalone = "-stangalone-"
-//
     archiveFileName = "${project.name}-${buildVersion}.${archiveExtension.getOrElse('.jar')}"
     manifest {
         attributes("Payapi-Version": buildVersion,
@@ -101,6 +97,9 @@
 
     implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5"
 
+    implementation group: 'io.swagger', name: 'swagger-annotations', version: swaggerVersion
+    implementation group: 'io.springfox', name: 'springfox-swagger2', version: springfoxVersion
+
     implementation "org.apache.commons:commons-lang3:${lang3Version}"
     implementation "net.javacrumbs.shedlock:shedlock-spring:${shedlockVersion}"
     implementation "net.javacrumbs.shedlock:shedlock-provider-redis-spring:${shedlockVersion}"
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/RFC3339DateFormat.java b/payapi/src/main/java/com/supwisdom/dlpay/RFC3339DateFormat.java
new file mode 100644
index 0000000..7ce517a
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/RFC3339DateFormat.java
@@ -0,0 +1,22 @@
+package com.supwisdom.dlpay;
+
+import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
+import com.fasterxml.jackson.databind.util.ISO8601Utils;
+
+import java.text.FieldPosition;
+import java.util.Date;
+
+
+public class RFC3339DateFormat extends ISO8601DateFormat {
+
+  private static final long serialVersionUID = 1L;
+
+  // Same as ISO8601DateFormat but serializing milliseconds.
+  @Override
+  public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
+    String value = ISO8601Utils.format(date, true);
+    toAppendTo.append(value);
+    return toAppendTo;
+  }
+
+}
\ No newline at end of file
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/ApiUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/api/ApiUtil.java
new file mode 100644
index 0000000..4a0abe7
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/ApiUtil.java
@@ -0,0 +1,19 @@
+package com.supwisdom.dlpay.api;
+
+import org.springframework.web.context.request.NativeWebRequest;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class ApiUtil {
+    public static void setExampleResponse(NativeWebRequest req, String contentType, String example) {
+        try {
+            HttpServletResponse res = req.getNativeResponse(HttpServletResponse.class);
+            res.setCharacterEncoding("UTF-8");
+            res.addHeader("Content-Type", contentType);
+            res.getWriter().print(example);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java
new file mode 100644
index 0000000..86c57a8
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java
@@ -0,0 +1,81 @@
+/**
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (4.2.2).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+package com.supwisdom.dlpay.api;
+
+import com.supwisdom.dlpay.payapi.model.QrcodePayConfirmRequest;
+import com.supwisdom.dlpay.payapi.model.QrcodePayInitRequest;
+import io.swagger.annotations.*;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.Valid;
+import javax.validation.constraints.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-12T16:17:20.799+08:00[Asia/Shanghai]")
+
+@Validated
+@Api(value = "consume", description = "the consume API")
+public interface ConsumeApi {
+
+    default Optional<NativeWebRequest> getRequest() {
+        return Optional.empty();
+    }
+
+    @ApiOperation(value = "", nickname = "qrcodePayConfirm", notes = "", tags={ "pos", })
+    @ApiResponses(value = { 
+        @ApiResponse(code = 200, message = "交易成功"),
+        @ApiResponse(code = 409, message = "交易正忙,稍后重试"),
+        @ApiResponse(code = 200, message = "交易失败") })
+    @RequestMapping(value = "/consume/qrcode/confirm",
+        produces = { "application/json" }, 
+        consumes = { "application/json" },
+        method = RequestMethod.POST)
+    default ResponseEntity<Void> qrcodePayConfirm(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-Tenant-Id", required=true) String xTenantId,@ApiParam(value = "Qrcode确认"  )  @Valid @RequestBody QrcodePayConfirmRequest qrcodePayConfirmRequest) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+
+    }
+
+
+    @ApiOperation(value = "", nickname = "qrcodePayInit", notes = "", tags={ "pos", })
+    @ApiResponses(value = { 
+        @ApiResponse(code = 200, message = "初始化成功"),
+        @ApiResponse(code = 200, message = "请求错误") })
+    @RequestMapping(value = "/consume/qrcode/init",
+        produces = { "application/json" }, 
+        consumes = { "application/json" },
+        method = RequestMethod.POST)
+    default ResponseEntity<Void> qrcodePayInit(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-Tenant-Id", required=true) String xTenantId,@ApiParam(value = "QrCode 初始化"  )  @Valid @RequestBody QrcodePayInitRequest qrcodePayInitRequest) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+
+    }
+
+
+    @ApiOperation(value = "", nickname = "qrcodePayQuery", notes = "根据系统交易参考号查询流水状态", tags={ "pos", })
+    @ApiResponses(value = { 
+        @ApiResponse(code = 200, message = "查询成功"),
+        @ApiResponse(code = 200, message = "查询失败") })
+    @RequestMapping(value = "/consume/qrcode/query/{refno}",
+        produces = { "application/json" }, 
+        method = RequestMethod.GET)
+    default ResponseEntity<Void> qrcodePayQuery(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-Tenant-Id", required=true) String xTenantId,@ApiParam(value = "系统交易参考号",required=true) @PathVariable("refno") String refno) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+
+    }
+
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApiController.java b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApiController.java
new file mode 100644
index 0000000..dc318fe
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApiController.java
@@ -0,0 +1,25 @@
+package com.supwisdom.dlpay.api;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.request.NativeWebRequest;
+import java.util.Optional;
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-12T16:09:45.966+08:00[Asia/Shanghai]")
+
+@Controller
+@RequestMapping("${openapi.aPITitle.base-path:/api}")
+public class ConsumeApiController implements ConsumeApi {
+
+    private final NativeWebRequest request;
+
+    @org.springframework.beans.factory.annotation.Autowired
+    public ConsumeApiController(NativeWebRequest request) {
+        this.request = request;
+    }
+
+    @Override
+    public Optional<NativeWebRequest> getRequest() {
+        return Optional.ofNullable(request);
+    }
+
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TAccount.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TAccount.java
index 7b04f82..4a06d9b 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TAccount.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TAccount.java
@@ -161,11 +161,11 @@
   }
 
   public Double getAvailbal() {
-    return availbal;
+    return balance - frozebal;
   }
 
   public void setAvailbal(Double availbal) {
-    this.availbal = availbal;
+    this.availbal = 0.0;
   }
 
   public Double getFrozebal() {
diff --git a/payapi/src/main/java/org/openapitools/configuration/HomeController.java b/payapi/src/main/java/org/openapitools/configuration/HomeController.java
new file mode 100644
index 0000000..2572783
--- /dev/null
+++ b/payapi/src/main/java/org/openapitools/configuration/HomeController.java
@@ -0,0 +1,19 @@
+package org.openapitools.configuration;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+
+/**
+ * Home redirection to OpenAPI api documentation
+ */
+@Controller
+public class HomeController {
+
+    @RequestMapping("/")
+    public String index() {
+        return "redirect:swagger-ui.html";
+    }
+
+
+}
diff --git a/payapi/src/main/java/org/openapitools/configuration/OpenAPIDocumentationConfig.java b/payapi/src/main/java/org/openapitools/configuration/OpenAPIDocumentationConfig.java
new file mode 100644
index 0000000..9754ed5
--- /dev/null
+++ b/payapi/src/main/java/org/openapitools/configuration/OpenAPIDocumentationConfig.java
@@ -0,0 +1,71 @@
+package org.openapitools.configuration;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import org.springframework.web.util.UriComponentsBuilder;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.paths.Paths;
+import springfox.documentation.spring.web.paths.RelativePathProvider;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import javax.servlet.ServletContext;
+
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-12T16:09:45.966+08:00[Asia/Shanghai]")
+
+@Configuration
+@EnableSwagger2
+public class OpenAPIDocumentationConfig {
+
+    ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+            .title("API Title")
+            .description("No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)")
+            .license("")
+            .licenseUrl("http://unlicense.org")
+            .termsOfServiceUrl("")
+            .version("1.0")
+            .contact(new Contact("","", ""))
+            .build();
+    }
+
+    @Bean
+    public Docket customImplementation(ServletContext servletContext, @Value("${openapi.aPITitle.base-path:/v1/api}") String basePath) {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .select()
+                    .apis(RequestHandlerSelectors.basePackage("com.supwisdom.dlpay.api"))
+                    .build()
+                .pathProvider(new BasePathAwareRelativePathProvider(servletContext, basePath))
+                .directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
+                .directModelSubstitute(java.time.OffsetDateTime.class, java.util.Date.class)
+                .apiInfo(apiInfo());
+    }
+
+    class BasePathAwareRelativePathProvider extends RelativePathProvider {
+        private String basePath;
+
+        public BasePathAwareRelativePathProvider(ServletContext servletContext, String basePath) {
+            super(servletContext);
+            this.basePath = basePath;
+        }
+
+        @Override
+        protected String applicationPath() {
+            return  Paths.removeAdjacentForwardSlashes(UriComponentsBuilder.fromPath(super.applicationPath()).path(basePath).build().toString());
+        }
+
+        @Override
+        public String getOperationPath(String operationPath) {
+            UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromPath("/");
+            return Paths.removeAdjacentForwardSlashes(
+                    uriComponentsBuilder.path(operationPath.replaceFirst("^" + basePath, "")).build().toString());
+        }
+    }
+
+}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
index 42ee1e4..be0b03e 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
@@ -28,6 +28,7 @@
 import org.springframework.cache.interceptor.KeyGenerator
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient
 import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.ComponentScan
 import org.springframework.context.annotation.Configuration
 import org.springframework.context.event.ContextStartedEvent
 import org.springframework.context.event.EventListener
@@ -243,6 +244,7 @@
 @ServletComponentScan
 @EnableAsync
 @EnableRetry
+@ComponentScan(basePackages = ["com.supwisdom.dlpay", "org.openapitools.configuration"])
 class PayApiApplication : SpringBootServletInitializer() {
 
     override fun configure(builder: SpringApplicationBuilder): SpringApplicationBuilder {
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
index 71c3189..3c03563 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
@@ -65,7 +65,6 @@
                     }
                     dtl.befbal = account.availbal
                     doRecalcAccountBalance(dtl, amount, account)
-                    account.availbal += amount
                     account.balance += amount
 
                     val sameDay = DateUtil.sameDay(account.lasttranstime,
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
index b0aefd2..6165d49 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
@@ -90,7 +90,7 @@
                         userid = builder.person().person!!.userid
                         accountNo = builder.person().person!!.accno
                         userName = builder.person().person!!.accname
-                        befbal = builder.person().person!!.availbal
+                        befbal = builder.person().person!!.balance
                     }
                     aftbal = 0.0
                     outtradeno = builder.outtradeno
diff --git a/payapi/src/main/resources/application.properties b/payapi/src/main/resources/application.properties
index 4c93ce7..7565c8c 100644
--- a/payapi/src/main/resources/application.properties
+++ b/payapi/src/main/resources/application.properties
@@ -49,6 +49,11 @@
 ###################################################
 spring.redis.database=0
 ###################################################
+springfox.documentation.swagger.v2.path=/api-docs
+openapi.aPITitle.base-path=/payapi
+spring.jackson.date-format=com.supwisdom.dlpay.payapi.RFC3339DateFormat
+spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
+
 multi-tenant.header.key=X-TENANT-ID
 multi-tenant.session.name=tenant-id
 multi-tenant.session.enableSessionScopedBean=false