refactor: 重构第三方交易服务前置
chore: 支付能力配置须判断是否实现服务
diff --git a/payapi/build.gradle b/payapi/build.gradle
index 2f1eed1..7e7d2ca 100644
--- a/payapi/build.gradle
+++ b/payapi/build.gradle
@@ -52,6 +52,13 @@
 
 docker.dependsOn(jar)
 
+configurations {
+    developmentOnly
+    runtimeClasspath {
+        extendsFrom developmentOnly
+    }
+}
+
 dependencies {
     implementation project(":payapi-common")
 
@@ -75,7 +82,7 @@
     implementation "org.springframework.cloud:spring-cloud-starter-netflix-hystrix"
     implementation "org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard"
 
-    runtime("org.springframework.boot:spring-boot-devtools")
+    developmentOnly("org.springframework.boot:spring-boot-devtools")
 
     implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5"
 
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/AgentPayServiceContext.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/AgentPayServiceContext.java
new file mode 100644
index 0000000..a17a1fc
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/AgentPayServiceContext.java
@@ -0,0 +1,31 @@
+package com.supwisdom.dlpay.agent;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class AgentPayServiceContext {
+  private final ApplicationContext applicationContext;
+
+  public AgentPayServiceContext(ApplicationContext applicationContext) {
+    this.applicationContext = applicationContext;
+  }
+
+  @SuppressWarnings("UNCHECKED_CAST")
+  public <T> AgentPayService<T> findAgentPayService(String service) {
+    try {
+      Object bean = applicationContext.getBean(service + "Agent");
+      if (bean instanceof AgentPayService) {
+        return (AgentPayService<T>) bean;
+      } else {
+        return null;
+      }
+    } catch (BeansException ex) {
+      log.error("can't create bean <{}> , ex {}", service, ex.getMessage());
+      return null;
+    }
+  }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProviderContext.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProviderContext.java
new file mode 100644
index 0000000..ef92fb5
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProviderContext.java
@@ -0,0 +1,25 @@
+package com.supwisdom.dlpay.agent;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class CheckFileProviderContext {
+  private final ApplicationContext applicationContext;
+
+  public CheckFileProviderContext(ApplicationContext applicationContext) {
+    this.applicationContext = applicationContext;
+  }
+
+  public CheckFileProvider findProvider(String name) {
+    try {
+      return (CheckFileProvider) applicationContext.getBean(name + "CheckFileProvider");
+    } catch (BeansException ex) {
+      log.error("未定义 sourcetype <$sourcetype> 对账处理 Provider");
+      return null;
+    }
+  }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/framework/util/ApplicationUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/framework/util/ApplicationUtil.java
deleted file mode 100644
index 7e51071..0000000
--- a/payapi/src/main/java/com/supwisdom/dlpay/framework/util/ApplicationUtil.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.supwisdom.dlpay.framework.util;
-
-import com.supwisdom.dlpay.agent.AgentPayService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.NoSuchBeanDefinitionException;
-import org.springframework.context.ApplicationContext;
-
-public class ApplicationUtil {
-  private static final Logger logger = LoggerFactory.getLogger(ApplicationUtil.class);
-
-  public static <T> AgentPayService<T> findAgentPayService(ApplicationContext context, String service) {
-    try {
-      Object bean = context.getBean(service);
-      if (bean instanceof AgentPayService) {
-        return (AgentPayService<T>) bean;
-      } else {
-        return null;
-      }
-    } catch (NoSuchBeanDefinitionException ex) {
-      logger.error("can't create bean <%s> , ex %s", service, ex.getMessage());
-      return null;
-    } catch (BeansException ex) {
-      logger.error("can't create bean <%s> , ex %s", service, ex.getMessage());
-      return null;
-    }
-  }
-}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/system/controller/ParamController.java b/payapi/src/main/java/com/supwisdom/dlpay/system/controller/ParamController.java
index 6d46a45..22f391e 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/system/controller/ParamController.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/system/controller/ParamController.java
@@ -1,5 +1,7 @@
 package com.supwisdom.dlpay.system.controller;
 
+import com.supwisdom.dlpay.agent.AgentPayServiceContext;
+import com.supwisdom.dlpay.agent.CheckFileProviderContext;
 import com.supwisdom.dlpay.api.bean.JsonResult;
 import com.supwisdom.dlpay.api.domain.TSourceType;
 import com.supwisdom.dlpay.api.domain.TSourceTypeConfig;
@@ -13,6 +15,7 @@
 import com.supwisdom.dlpay.system.service.ParamService;
 import com.supwisdom.dlpay.util.ConstantUtil;
 import com.supwisdom.dlpay.util.WebCheckException;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
@@ -22,18 +25,25 @@
 import java.util.List;
 import java.util.Map;
 
+@Slf4j
 @Controller
 public class ParamController {
   private final ParamService paramService;
   private final SystemUtilService systemUtilService;
   private final DictionaryProxy dictionaryProxy;
+  private final CheckFileProviderContext checkFileProviderContext;
+  private final AgentPayServiceContext agentPayServiceContext;
 
   public ParamController(ParamService paramService,
                          SystemUtilService systemUtilService,
-                         DictionaryProxy dictionaryProxy) {
+                         DictionaryProxy dictionaryProxy,
+                         CheckFileProviderContext checkFileProviderContext,
+                         AgentPayServiceContext agentPayServiceContext) {
     this.paramService = paramService;
     this.systemUtilService = systemUtilService;
     this.dictionaryProxy = dictionaryProxy;
+    this.checkFileProviderContext = checkFileProviderContext;
+    this.agentPayServiceContext = agentPayServiceContext;
   }
 
   @GetMapping("/param/syspara")
@@ -410,26 +420,41 @@
         if (state.equals(tPaytype.getChargeEnable())) {
           return JsonResult.error("状态错误,请重新查询后操作");
         }
+        if (state && agentPayServiceContext.findAgentPayService(sourcetype) == null) {
+          return JsonResult.error("支付类型未实现充值");
+        }
         tPaytype.setChargeEnable(state);
       } else if ("consume".equals(optype)) {
         if (state.equals(tPaytype.getConsumeEnable())) {
           return JsonResult.error("状态错误,请重新查询后操作");
         }
+        if (state && agentPayServiceContext.findAgentPayService(sourcetype) == null) {
+          return JsonResult.error("支付类型未实现支付");
+        }
         tPaytype.setConsumeEnable(state);
       } else if ("anonymous".equals(optype)) {
         if (state.equals(tPaytype.getAnonymousEnable())) {
           return JsonResult.error("状态错误,请重新查询后操作");
         }
+        if (state && agentPayServiceContext.findAgentPayService(sourcetype) == null) {
+          return JsonResult.error("支付类型未实现支付");
+        }
         tPaytype.setAnonymousEnable(state);
       } else if ("reversable".equals(optype)) {
         if (state.equals(tPaytype.getReversable())) {
           return JsonResult.error("状态错误,请重新查询后操作");
         }
+        if (state && agentPayServiceContext.findAgentPayService(sourcetype) == null) {
+          return JsonResult.error("支付类型未实现支付");
+        }
         tPaytype.setReversable(state);
       } else if ("checkable".equals(optype)) {
         if (state.equals(tPaytype.getCheckable())) {
           return JsonResult.error("状态错误,请重新查询后操作");
         }
+        if (state && checkFileProviderContext.findProvider(sourcetype) == null) {
+          return JsonResult.error("支付类型未实现对账业务");
+        }
         tPaytype.setCheckable(state);
       } else {
         if (state.equals(tPaytype.getEnable())) {
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
index f16956f..fd908eb 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
@@ -2,6 +2,7 @@
 
 import com.supwisdom.dlpay.framework.tenant.TenantCacheKeyGen
 import io.lettuce.core.ReadFrom
+import mu.KotlinLogging
 import net.javacrumbs.shedlock.core.LockProvider
 import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider
 import org.springframework.beans.factory.annotation.Autowired
@@ -19,6 +20,8 @@
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient
 import org.springframework.context.annotation.Bean
 import org.springframework.context.annotation.Configuration
+import org.springframework.context.event.ContextStartedEvent
+import org.springframework.context.event.EventListener
 import org.springframework.data.redis.connection.RedisConnectionFactory
 import org.springframework.data.redis.connection.RedisPassword
 import org.springframework.data.redis.connection.RedisStandaloneConfiguration
@@ -46,7 +49,7 @@
     @Bean
     fun redisConnectionFactory(): LettuceConnectionFactory {
         val clientConfig = LettuceClientConfiguration.builder()
-                .readFrom(ReadFrom.SLAVE_PREFERRED)
+                .readFrom(ReadFrom.REPLICA_PREFERRED)
                 .build()
         val serverConfig = RedisStandaloneConfiguration(redis.host, redis.port)
         if (redis.password.isNotEmpty()) {
@@ -149,6 +152,15 @@
 }
 
 
+@Component
+class TenantConfigListener {
+    private val log = KotlinLogging.logger { }
+    @EventListener
+    fun handleContextStarted(evt: ContextStartedEvent) {
+        log.info { "Service started check tenant information" }
+    }
+}
+
 @SpringBootApplication
 @EnableDiscoveryClient
 @EnableScheduling
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 e771add..10541a8 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
@@ -1,6 +1,7 @@
 package com.supwisdom.dlpay.api
 
 import com.supwisdom.dlpay.agent.AgentCode
+import com.supwisdom.dlpay.agent.AgentPayServiceContext
 import com.supwisdom.dlpay.agent.DtlStatus
 import com.supwisdom.dlpay.agent.citizencard.YnrccUtil
 import com.supwisdom.dlpay.api.domain.TDtlQuery
@@ -13,15 +14,12 @@
 import com.supwisdom.dlpay.framework.service.DayendSettleService
 import com.supwisdom.dlpay.framework.service.SystemUtilService
 import com.supwisdom.dlpay.framework.tenant.TenantContext
-import com.supwisdom.dlpay.framework.util.ApplicationUtil
 import com.supwisdom.dlpay.framework.util.Constants
 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.aop.interceptor.AsyncUncaughtExceptionHandler
 import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.context.ApplicationContext
 import org.springframework.context.annotation.Bean
 import org.springframework.context.annotation.Configuration
 import org.springframework.scheduling.annotation.Async
@@ -106,7 +104,7 @@
     lateinit var transactionService: TransactionServiceProxy
 
     @Autowired
-    private lateinit var applicationContext: ApplicationContext
+    private lateinit var agentPayServiceContext: AgentPayServiceContext
 
     @Autowired
     private lateinit var dtlQueryResultService: DtlQueryResultService
@@ -135,8 +133,7 @@
                 return
             }
 
-            val service = ApplicationUtil.findAgentPayService<Any>(applicationContext,
-                    transaction.sourceType + "Agent")
+            val service = agentPayServiceContext.findAgentPayService<Any>(transaction.sourceType)
 
             logger.info("refno=[${transaction.refno}]开始第" + (qcnt + 1) + "次查询支付结果:")
             val resp = service.query(transaction)
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/charge_api_controller.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/charge_api_controller.kt
index 939f27a..a4cac87 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/charge_api_controller.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/controller/charge_api_controller.kt
@@ -162,7 +162,8 @@
         }
 
         val shopacc = accountUtilServcie.readShopbyShopaccno(param.merchant)
-        if (shopacc.shop.shoptype != ShopTypes.DEPOSIT.value()) {
+        if (shopacc.shop.shoptype != ShopTypes.DEPOSIT.value()
+                || shopacc.shop.status != TradeDict.STATUS_NORMAL) {
             return ResponseBodyBuilder.failEntity(response, TradeErrorCode.INPUT_DATA_ERROR,
                     "该商户不能充值")
         }
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 f10bf3b..7cad44b 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
@@ -2,10 +2,13 @@
 
 import com.supwisdom.dlpay.agent.AgentCode
 import com.supwisdom.dlpay.agent.AgentPayService
+import com.supwisdom.dlpay.agent.AgentPayServiceContext
 import com.supwisdom.dlpay.agent.DtlStatus
 import com.supwisdom.dlpay.agent.domain.QrcodePayTrans
 import com.supwisdom.dlpay.agent.service.AgentServiceProxy
-import com.supwisdom.dlpay.api.*
+import com.supwisdom.dlpay.api.AccountProxy
+import com.supwisdom.dlpay.api.AgentQueryResultTask
+import com.supwisdom.dlpay.api.TransactionBuilder
 import com.supwisdom.dlpay.api.bean.*
 import com.supwisdom.dlpay.api.bean.groups.ConfirmAction
 import com.supwisdom.dlpay.api.bean.groups.InitAction
@@ -14,11 +17,9 @@
 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 org.apache.commons.lang3.StringUtils
 import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.context.ApplicationContext
 import org.springframework.data.redis.core.RedisTemplate
 import org.springframework.http.ResponseEntity
 import org.springframework.validation.annotation.Validated
@@ -42,7 +43,7 @@
     @Autowired
     lateinit var cardService: CardService
     @Autowired
-    private lateinit var applicationContext: ApplicationContext
+    private lateinit var agentPayServiceContext: AgentPayServiceContext
 
     @Autowired
     private lateinit var sourceTypeService: SourceTypeService
@@ -219,7 +220,7 @@
     }
 
     fun <T> createAgentService(sourceType: String): AgentPayService<T> {
-        return ApplicationUtil.findAgentPayService<T>(applicationContext, sourceType + "Agent")
+        return agentPayServiceContext.findAgentPayService(sourceType)
                 ?: throw TransactionCheckException(TradeErrorCode.BUSINESS_DEAL_ERROR,
                         "支付类型<$sourceType>未定义")
     }
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.kt
index 7da32f1..eec664a 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.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.CheckFileProviderContext
 import com.supwisdom.dlpay.api.domain.TSourceType
 import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
 import com.supwisdom.dlpay.api.domain.TTransactionChkfile
@@ -237,7 +238,7 @@
                          val code: String, val message: String);
 
     @Autowired
-    private lateinit var applicationContext: ApplicationContext
+    private lateinit var checkFileProviderContext: CheckFileProviderContext
 
     @Autowired
     private lateinit var transactionReconciliationService: TransactionReconciliationService
@@ -249,7 +250,7 @@
 
     private fun getProvider(sourcetype: String): CheckFileProvider? {
         return try {
-            applicationContext.getBean("${sourcetype}CheckFileProvider") as CheckFileProvider
+            checkFileProviderContext.findProvider(sourcetype)
         } catch (ex: BeansException) {
             logger.error { "未定义 sourcetype <$sourcetype> 对账处理 Provider" }
             null
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt
index 1d8e2fc..7b477dd 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_task.kt
@@ -1,6 +1,7 @@
 package com.supwisdom.dlpay.api
 
 import com.supwisdom.dlpay.agent.AgentCode
+import com.supwisdom.dlpay.agent.AgentPayServiceContext
 import com.supwisdom.dlpay.agent.DtlStatus
 import com.supwisdom.dlpay.api.domain.TDtlQuery
 import com.supwisdom.dlpay.api.domain.TShopdtl
@@ -9,12 +10,10 @@
 import com.supwisdom.dlpay.api.service.DtlQueryResultService
 import com.supwisdom.dlpay.api.service.TransactionServiceProxy
 import com.supwisdom.dlpay.framework.service.SystemUtilService
-import com.supwisdom.dlpay.framework.util.ApplicationUtil
 import com.supwisdom.dlpay.framework.util.TradeDict
 import com.supwisdom.dlpay.util.ConstantUtil
 import net.javacrumbs.shedlock.core.SchedulerLock
 import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.context.ApplicationContext
 import org.springframework.scheduling.annotation.Scheduled
 import org.springframework.stereotype.Component
 import org.springframework.stereotype.Service
@@ -55,7 +54,7 @@
     @Autowired
     lateinit var consumePayService: ConsumePayService
     @Autowired
-    private lateinit var applicationContext: ApplicationContext
+    private lateinit var agentPayServiceContext: AgentPayServiceContext
 
     @Scheduled(cron = "\${query.third.transdtl.result.cron:-}")
     @SchedulerLock(name = "DtlQueryResultSchedulerTask", lockAtMostForString = "PT10M")
@@ -101,7 +100,7 @@
             return
         }
 
-        val service = ApplicationUtil.findAgentPayService<Any>(applicationContext, dtl.sourceType + "Agent")
+        val service = agentPayServiceContext.findAgentPayService<Any>(dtl.sourceType)
         val resp = service.query(dtl)
         when (resp.code) {
             AgentCode.SUCCESS -> {