实现对账框架,但未完成测试
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java
new file mode 100644
index 0000000..964b460
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java
@@ -0,0 +1,9 @@
+package com.supwisdom.dlpay.agent;
+
+public interface CheckFileProvider {
+ AgentResponse acquireCheckFile(String checkDate);
+
+ AgentResponse queryCheckFile(String checkDate);
+
+ AgentResponse downloadCheckFile(String checkDate);
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/SourceTypeCheckDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/SourceTypeCheckDao.java
new file mode 100644
index 0000000..361cb42
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/SourceTypeCheckDao.java
@@ -0,0 +1,11 @@
+package com.supwisdom.dlpay.api.dao;
+
+import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface SourceTypeCheckDao extends CrudRepository<TSourceTypeCheckStatus, Integer> {
+ TSourceTypeCheckStatus getBySourceTypeAndTenantId(String sourceType, String tenantid);
+
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/SourceTypeDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/SourceTypeDao.java
index b823f3d..38e1b38 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/SourceTypeDao.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/SourceTypeDao.java
@@ -20,4 +20,6 @@
@Query("select t from TSourceType t")
List<TSourceType> getConsumeSourceTypes();
+
+ List<TSourceType> findByEnableAndTenantid(Boolean enabled, String tenantid);
}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceType.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceType.java
index d20efaf..49f1d81 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceType.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceType.java
@@ -58,6 +58,10 @@
@Column(name = "PAYDESC", length = 200)
private String paydesc;
+
+ @Column(name = "tplusn", precision = 5)
+ private Integer tplusN; // 系统结算日期, T+0 ~ T+N
+
@Column(name = "tenantid", length = 20)
@NotNull
private String tenantid = "";
@@ -167,4 +171,12 @@
public void setDepositeSubjno(String depositeSubjno) {
this.depositeSubjno = depositeSubjno;
}
+
+ public Integer getTplusN() {
+ return tplusN;
+ }
+
+ public void setTplusN(Integer tplusN) {
+ this.tplusN = tplusN;
+ }
}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java
new file mode 100644
index 0000000..4a4f41c
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java
@@ -0,0 +1,130 @@
+package com.supwisdom.dlpay.api.domain;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.sql.Timestamp;
+
+@Entity
+@Table(name = "tb_sourcetype_check",
+ indexes = {@Index(name = "sourcetype_check_idx", columnList = "source_type, tenantid", unique = true)})
+public class TSourceTypeCheckStatus {
+ @Id
+ @SequenceGenerator(name = "st_checker_id", sequenceName = "SEQ_SOURCETYPE_CHECK", allocationSize = 1)
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "st_checker_id")
+ private Integer id;
+
+ @Column(name = "source_type", length = 30)
+ @NotNull
+ private String sourceType;
+
+ @Column(name = "start_accdate", length = 8)
+ @NotNull
+ private String startAccdate; // 对账起始日期
+
+ @Column(name = "check_file_date", length = 8)
+ private String checkFileDate; // 当前对账文件日期
+
+ @Column(name = "check_file_status")
+ private Boolean checkFileOk; // 当前对账文件下载成功标志
+
+ @Column(name = "check_accdate", length = 8)
+ @NotNull
+ private String checkAccdate; // 当前对账记账日期
+
+ @Column(name = "check_status")
+ @NotNull
+ private Boolean checkStatus; // 当前对账完成状态
+
+ @Column(name = "force_recheck")
+ @NotNull
+ private Boolean forceRecheck; // 是否对当前日期强制对账
+
+ @Column(name = "last_update")
+ @NotNull
+ @Version
+ private Timestamp lastUpdate;
+
+ @Column(name = "tenantid", length = 20)
+ @NotNull
+ private String tenantId;
+
+ 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 getCheckAccdate() {
+ return checkAccdate;
+ }
+
+ public void setCheckAccdate(String checkAccdate) {
+ this.checkAccdate = checkAccdate;
+ }
+
+ public Boolean getForceRecheck() {
+ return forceRecheck;
+ }
+
+ public void setForceRecheck(Boolean forceRecheck) {
+ this.forceRecheck = forceRecheck;
+ }
+
+ public Timestamp getLastUpdate() {
+ return lastUpdate;
+ }
+
+ public void setLastUpdate(Timestamp lastUpdate) {
+ this.lastUpdate = lastUpdate;
+ }
+
+ public String getTenantId() {
+ return tenantId;
+ }
+
+ public void setTenantId(String tenantId) {
+ this.tenantId = tenantId;
+ }
+
+ public String getStartAccdate() {
+ return startAccdate;
+ }
+
+ public void setStartAccdate(String startAccdate) {
+ this.startAccdate = startAccdate;
+ }
+
+ public String getCheckFileDate() {
+ return checkFileDate;
+ }
+
+ public void setCheckFileDate(String checkFileDate) {
+ this.checkFileDate = checkFileDate;
+ }
+
+ public Boolean getCheckFileOk() {
+ return checkFileOk;
+ }
+
+ public void setCheckFileOk(Boolean checkFileOk) {
+ this.checkFileOk = checkFileOk;
+ }
+
+ public Boolean getCheckStatus() {
+ return checkStatus;
+ }
+
+ public void setCheckStatus(Boolean checkStatus) {
+ this.checkStatus = checkStatus;
+ }
+}
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 833eb0a..c0d78b7 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
@@ -1,9 +1,11 @@
package com.supwisdom.dlpay.api.service;
import com.supwisdom.dlpay.api.domain.TSourceType;
+import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
+import java.util.List;
import java.util.Map;
/**
@@ -23,6 +25,9 @@
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
boolean checkShopSourceType(String shopaccno, String sourceType, boolean anonymousflag) throws Exception;
+ @Transactional
+ List<TSourceType> getAllEnabledSourcetype();
+
/**
* 获取支付能力充值参数全局配置
*/
@@ -41,4 +46,10 @@
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
boolean checkShopCanReverse(String sourcetype, String shopaccno) throws Exception;
+ @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
+ TSourceTypeCheckStatus getSourceTypeCheckStatus(String sourceType);
+
+ @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
+ TSourceTypeCheckStatus saveOrUpdateSourceTypeCheckStatus(TSourceTypeCheckStatus s);
+
}
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 96f3656..545f848 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
@@ -1,15 +1,10 @@
package com.supwisdom.dlpay.api.service.impl;
-import com.supwisdom.dlpay.api.dao.ShopSourceTypeConfigDao;
-import com.supwisdom.dlpay.api.dao.ShopSourceTypeDao;
-import com.supwisdom.dlpay.api.dao.SourceTypeConfigDao;
-import com.supwisdom.dlpay.api.dao.SourceTypeDao;
-import com.supwisdom.dlpay.api.domain.TShopSourceType;
-import com.supwisdom.dlpay.api.domain.TShopSourceTypeConfig;
-import com.supwisdom.dlpay.api.domain.TSourceType;
-import com.supwisdom.dlpay.api.domain.TSourceTypeConfig;
+import com.supwisdom.dlpay.api.dao.*;
+import com.supwisdom.dlpay.api.domain.*;
import com.supwisdom.dlpay.api.service.SourceTypeService;
import com.supwisdom.dlpay.exception.TransactionProcessException;
+import com.supwisdom.dlpay.framework.tenant.TenantContext;
import com.supwisdom.dlpay.framework.util.StringUtil;
import com.supwisdom.dlpay.framework.util.TradeErrorCode;
import org.springframework.beans.factory.annotation.Autowired;
@@ -29,14 +24,16 @@
private final SourceTypeConfigDao sourceTypeConfigDao;
private final ShopSourceTypeDao shopSourceTypeDao;
private final ShopSourceTypeConfigDao shopSourceTypeConfigDao;
+ private final SourceTypeCheckDao sourceTypeCheckDao;
@Autowired
public SourceTypeServiceImpl(SourceTypeDao sourceTypeDao, SourceTypeConfigDao sourceTypeConfigDao,
- ShopSourceTypeDao shopSourceTypeDao, ShopSourceTypeConfigDao shopSourceTypeConfigDao) {
+ ShopSourceTypeDao shopSourceTypeDao, ShopSourceTypeConfigDao shopSourceTypeConfigDao, SourceTypeCheckDao sourceTypeCheckDao) {
this.sourceTypeDao = sourceTypeDao;
this.sourceTypeConfigDao = sourceTypeConfigDao;
this.shopSourceTypeDao = shopSourceTypeDao;
this.shopSourceTypeConfigDao = shopSourceTypeConfigDao;
+ this.sourceTypeCheckDao = sourceTypeCheckDao;
}
@@ -196,4 +193,21 @@
return true;
}
+ @Override
+ public List<TSourceType> getAllEnabledSourcetype() {
+ return sourceTypeDao.findByEnableAndTenantid(true, TenantContext.getTenantSchema());
+ }
+
+ @Override
+ public TSourceTypeCheckStatus getSourceTypeCheckStatus(String sourceType) {
+ return sourceTypeCheckDao.getBySourceTypeAndTenantId(sourceType, TenantContext.getTenantSchema());
+ }
+
+ @Override
+ public TSourceTypeCheckStatus saveOrUpdateSourceTypeCheckStatus(TSourceTypeCheckStatus s) {
+ if (s.getTenantId() == null || s.getTenantId().isEmpty()) {
+ s.setTenantId(TenantContext.getTenantSchema());
+ }
+ return sourceTypeCheckDao.save(s);
+ }
}
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 759e047..65f41d8 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
@@ -40,6 +40,15 @@
}
}
+ @Bean(name = ["sourcetypeCheckTaskExecutor"])
+ fun threadPoolTaskExecutor(): Executor {
+ return ThreadPoolTaskExecutor().apply {
+ corePoolSize = 5
+ maxPoolSize = 10
+ setWaitForTasksToCompleteOnShutdown(true)
+ }
+ }
+
override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler? {
return MyAsyncUncaughtExceptionHandler()
}
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
new file mode 100644
index 0000000..5e72e70
--- /dev/null
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_sourcetype_chk.kt
@@ -0,0 +1,199 @@
+package com.supwisdom.dlpay.api
+
+import com.supwisdom.dlpay.agent.AgentCode
+import com.supwisdom.dlpay.agent.CheckFileProvider
+import com.supwisdom.dlpay.api.domain.TSourceType
+import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
+import com.supwisdom.dlpay.api.service.SourceTypeService
+import com.supwisdom.dlpay.framework.service.SystemUtilService
+import com.supwisdom.dlpay.framework.util.DateUtil
+import mu.KotlinLogging
+import net.javacrumbs.shedlock.core.SchedulerLock
+import org.springframework.beans.BeansException
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.context.ApplicationContext
+import org.springframework.scheduling.annotation.Async
+import org.springframework.scheduling.annotation.AsyncResult
+import org.springframework.scheduling.annotation.Scheduled
+import org.springframework.stereotype.Component
+import java.util.concurrent.Future
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+
+@Component
+class SourceTypeCheck {
+ private val logger = KotlinLogging.logger { }
+
+ @Autowired
+ private lateinit var sourceTypeService: SourceTypeService
+
+ @Autowired
+ private lateinit var systemUtilService: SystemUtilService
+
+ @Autowired
+ private lateinit var sourceTypeCheckExecutor: SourceTypeCheckExecutor
+
+ private fun newSourceTypeStatus(sourceType: TSourceType): TSourceTypeCheckStatus {
+ return TSourceTypeCheckStatus().also {
+ it.sourceType = sourceType.sourceType
+ it.tenantId = sourceType.tenantid
+ it.startAccdate = systemUtilService.accdate
+ it.forceRecheck = false
+ it.checkFileDate = it.startAccdate
+ it.checkFileOk = false
+ }.let {
+ sourceTypeService.saveOrUpdateSourceTypeCheckStatus(it)
+ }
+ }
+
+ private fun determineSourceTypeCheck(sourcetype: TSourceType): TSourceTypeCheckStatus? {
+ if (!sourcetype.checkable) {
+ return null
+ }
+ val status = sourceTypeService.getSourceTypeCheckStatus(sourcetype.sourceType)
+ ?: newSourceTypeStatus(sourcetype)
+
+ // 小于对账日期
+ val interval = DateUtil.getIntervalDay(status.checkAccdate, systemUtilService.accdate)
+ // 已对账
+ if (status.checkStatus) {
+ // 大于等于结算日期进行对账
+ return if (interval >= sourcetype.tplusN) {
+ status.checkAccdate = DateUtil.getNewDay(status.checkAccdate, 1)
+ status.checkStatus = false
+ status.checkFileOk = false
+ status.checkFileDate = DateUtil.getNewDay(status.checkFileDate, 1)
+ sourceTypeService.saveOrUpdateSourceTypeCheckStatus(status)
+ } else {
+ null
+ }
+ }
+ return status
+ }
+
+ @Scheduled(cron = "\${payapi.sourcetype.checker.scheduler:-}")
+ @SchedulerLock(name = "payapiSourceTypeCheckLock", lockAtMostForString = "PT30M")
+ fun runCheck() {
+ val allSourcetype = sourceTypeService.allEnabledSourcetype
+ ?: return
+
+ val checkFileStatus = allSourcetype.mapNotNull {
+ determineSourceTypeCheck(it)
+ }
+
+ val checkProcessResult = checkFileStatus.map {
+ // 对账文件是否下载完成
+ if (it.checkFileOk) {
+ // 开始对账
+ sourceTypeCheckExecutor.reconciliation(it)
+ } else {
+ // 开始下载对账文件
+ sourceTypeCheckExecutor.checkSourceType(it)
+ }
+ }.toMutableList()
+
+ while (checkProcessResult.isNotEmpty()) {
+ val finishResult: MutableList<Future<SourceTypeCheckExecutor.ExecutorResult>> = mutableListOf()
+ val newPrcResult: MutableList<Future<SourceTypeCheckExecutor.ExecutorResult>> = mutableListOf()
+ checkProcessResult.forEach {
+ try {
+ val result = it.get(10, TimeUnit.SECONDS)
+ if (result.code == SourceTypeCheckExecutor.SUCCESS) {
+ // 完成对账问价下载 但 还未对账
+ if (result.status.checkFileOk && !result.status.checkStatus) {
+ newPrcResult.add(sourceTypeCheckExecutor.reconciliation(result.status))
+ } else if (!result.status.checkFileOk) {
+ logger.error {
+ "sourcetype <${result.status.sourceType}> get checkfile error," +
+ "<${result.message}>"
+ }
+ } else if (result.status.checkFileOk && result.status.checkStatus) {
+ logger.info {
+ "sourcetype <${result.status.sourceType}> " +
+ "date <${result.status.checkAccdate}> 对账成功"
+ }
+ }
+ } else {
+ logger.error {
+ "sourcetype <${result.status.sourceType}> 对账状任务错误, error=" +
+ "<${result.message}>"
+ }
+ }
+ finishResult.add(it)
+ } catch (ex: TimeoutException) {
+ // async task not finished
+ }
+ }
+ checkProcessResult.removeIf { finishResult.contains(it) }
+ finishResult.clear()
+ checkProcessResult.addAll(newPrcResult)
+ }
+ }
+}
+
+@Component
+class SourceTypeCheckExecutor {
+ companion object {
+ const val SUCCESS = "success"
+ const val FAIL = "fail"
+ const val WAIT = "wait"
+ }
+
+ class ExecutorResult(val status: TSourceTypeCheckStatus,
+ val code: String, val message: String);
+
+ @Autowired
+ private lateinit var applicationContext: ApplicationContext
+
+ private val logger = KotlinLogging.logger { }
+
+ private fun getProvider(sourcetype: String): CheckFileProvider? {
+ return try {
+ applicationContext.getBean("${sourcetype}CheckFileProvider") as CheckFileProvider
+ } catch (ex: BeansException) {
+ logger.error { "未定义 sourcetype <$sourcetype> 对账处理 Provider" }
+ null
+ }
+ }
+
+ private fun downloadFile(provider: CheckFileProvider, checkStatus: TSourceTypeCheckStatus): ExecutorResult {
+ return provider.downloadCheckFile(checkStatus.checkFileDate).let { resp ->
+ when {
+ resp.code == AgentCode.SUCCESS -> ExecutorResult(checkStatus, SUCCESS, "成功")
+ resp.code == AgentCode.REQUIRE_QUERY -> ExecutorResult(checkStatus, WAIT, "等待查询对账文件")
+ else -> ExecutorResult(checkStatus, FAIL, resp.agentMsg)
+ }
+ }
+ }
+
+ @Async("sourcetypeCheckTaskExecutor")
+ fun checkSourceType(checkStatus: TSourceTypeCheckStatus): Future<ExecutorResult> {
+ // 2. 根据对账日期下载对账文件
+ val result = getProvider(checkStatus.sourceType)?.let { provider ->
+ val acResp = provider.acquireCheckFile(checkStatus.checkFileDate)
+ if (acResp.code == AgentCode.REQUIRE_QUERY) {
+ var success = false
+ repeat(10) {
+ if (!success) {
+ Thread.sleep(3000)
+ success = provider.queryCheckFile(checkStatus.checkFileDate).code == AgentCode.SUCCESS
+ }
+ }
+ if (success) {
+ downloadFile(provider, checkStatus)
+ } else {
+ ExecutorResult(checkStatus, FAIL, "下载失败,未能查到对账文件")
+ }
+ } else {
+ downloadFile(provider, checkStatus)
+ }
+ } ?: ExecutorResult(checkStatus, FAIL, "未定义 CheckFileProvider")
+ return AsyncResult(result)
+ }
+
+ @Async("sourcetypeCheckTaskExecutor")
+ fun reconciliation(checkStatus: TSourceTypeCheckStatus): Future<ExecutorResult> {
+ // 3. 完成对账
+ return AsyncResult(ExecutorResult(checkStatus, SUCCESS, "成功"))
+ }
+}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_ynrccchk_task.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_ynrccchk_task.kt
index 067f1ef..e7ad75f 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_ynrccchk_task.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/scheduler_ynrccchk_task.kt
@@ -52,8 +52,6 @@
@SchedulerLock(name = "DownloadYnrccChkfileSchedulerTask", lockAtMostForString = "PT10M")
fun doDownloadYnrccChkfile() {
try {
- if(null==TenantContext.getTenantSchema()) TenantContext.setTenantSchema(Constants.DEFAULT_TENANTID)
-
//下载对账单逻辑
val hostdate = systemUtilService.sysdatetime.hostdate
val downloadLastdate = ynrccBusinessService.getLastDownloadBillDate()
@@ -63,7 +61,7 @@
for (i in 1 until diffDays) {
val billDate = DateUtil.getNewDay(downloadLastdate, i) //要取对账单的日期
- var chkfile = transactionReconciliationService.doInitTransactionChkfile(billDate, TradeDict.PAYTYPE_CITIZEN_CARD)
+ val chkfile = transactionReconciliationService.doInitTransactionChkfile(billDate, TradeDict.PAYTYPE_CITIZEN_CARD)
val resp = citizencardPayService.getChkfilename(billDate, null)
if (YnrccUtil.CODE_SUCCESS == resp.code) {
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 173d15a..8147a59 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
@@ -22,6 +22,6 @@
fun doBatchSaveYnrccTransactionChkDtl(chkfile: TTransactionChkfile, list: ArrayList<YnrccChkfileBean>): Boolean
@Transactional(rollbackFor = [Exception::class])
- fun doSuccessTransactionChkfile(chkfile: TTransactionChkfile, reamrk:String)
+ fun doSuccessTransactionChkfile(chkfile: TTransactionChkfile, remark:String)
}
\ No newline at end of file