refactor: 重构了对账任务
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java b/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java
index b8b3fa5..008dcd7 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/agent/CheckFileProvider.java
@@ -3,9 +3,25 @@
import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus;
public interface CheckFileProvider {
+ /**
+ * 请求对账文件接口,由对账任务请求第三查询 checkStatus.checkFileDate 的对账文件
+ *
+ * @param checkStatus
+ * @return AgentResponse.code == SUCCESS 时 ,有两种情况: 当 checkStatus.checkFileOk == true 表示对账文件明细已经
+ * 写入数据库中,不需要 downloadCheckFile , 当为 false 时才需要
+ * AgentResponse.code == REQUIRE_QUERY 时, 对账任务会调用 queryCheckFile 方法判断是否可以下载对账文件了
+ * 其它值表示失败
+ */
AgentResponse<TSourceTypeCheckStatus> acquireCheckFile(TSourceTypeCheckStatus checkStatus);
AgentResponse<TSourceTypeCheckStatus> queryCheckFile(TSourceTypeCheckStatus checkStatus);
+ /**
+ * 下载checkStatus.checkDate 指定的日期的对账文件,由对账任务调用,
+ * 如果 checkStatus.chkFileUrl == 'none' 表示当日没有对账文件,
+ *
+ * @param checkStatus
+ * @return AgentResponse.code 返回值只有 SUCCESS 和 FAIL
+ */
AgentResponse<TSourceTypeCheckStatus> downloadCheckFile(TSourceTypeCheckStatus checkStatus);
}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionChkdtlDao.java b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionChkdtlDao.java
index 122ce76..51301ce 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionChkdtlDao.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/dao/TransactionChkdtlDao.java
@@ -23,9 +23,19 @@
CountAmountBean getTransactionSumInfo(String accdate, String sourcetype);
@Query("select a from TTransactionChkdtl a,TPersondtl b where a.localRefno=b.refno and a.sourcetype=b.sourceType and a.accdate=b.accdate and a.amount=b.amount " +
- " and a.otherStatus='success' and b.status='success' order by a.recordno ")
+ " and a.otherStatus='success' and b.status='success' and a.chkresult <> 'equal' and a.chkfileId=?1 order by a.recordno ")
List<TTransactionChkdtl> getCheckEqualDetails(String chkfileId);
+ @Query("select a from TTransactionChkdtl a,TPersondtl b where a.localRefno=b.refno and a.sourcetype=b.sourceType and a.accdate=b.accdate and a.amount=b.amount " +
+ " and a.otherStatus='success' and b.status='success' and a.chkresult <> 'equal' and a.chkfileId=?1 and a.recordno between ?2 and ?3 order by a.recordno ")
+ List<TTransactionChkdtl> findEqualDtlWithLimit(String chkfileId, Integer start, Integer end);
+
+ @Query("select min(a.recordno) from TTransactionChkdtl a where a.chkfileId=?1 and a.chkresult=?2")
+ Integer getMinRecordNoByStatus(String chkFileId, String status);
+
+ @Query("select min(a.recordno) from TTransactionChkdtl a where a.chkfileId=?1 ")
+ Integer getMinRecordNo(String chkFileId);
+
@Query("from TTransactionChkdtl t where t.chkfileId=?1 and t.chkresult='uncheck' order by t.recordno ")
List<TTransactionChkdtl> getUncheckTransactionChkdtls(String chkfileId);
@@ -34,6 +44,6 @@
@Query(value = "select t.refno from TB_PERSONDTL t left join TB_TRANSACTION_CHKDTL a on t.REFNO=a.OTHER_REFNO and a.CHKFILE_ID=:chkfileId " +
" where t.ACCDATE=:accdate and t.SOURCETYPE=:sourcetype and t.status='success' and a.id is null order by t.refno ", nativeQuery = true)
- List<String> findLocalMoreDtls(@Param("accdate")String accdate, @Param("sourcetype")String sourcetype, @Param("chkfileId") String chkfileId);
+ List<String> findLocalMoreDtls(@Param("accdate") String accdate, @Param("sourcetype") String sourcetype, @Param("chkfileId") String chkfileId);
}
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
index bad55b9..6a13467 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TSourceTypeCheckStatus.java
@@ -1,5 +1,7 @@
package com.supwisdom.dlpay.api.domain;
+import com.sun.org.apache.xpath.internal.operations.Bool;
+
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.sql.Timestamp;
@@ -35,6 +37,13 @@
@NotNull
private Boolean checkStatus; // 当前对账完成状态
+ @Column(name = "repair_status")
+ private Boolean repairStatus; // 补帐完成状态
+
+ @Column(name = "settle_status")
+ @NotNull
+ private Boolean settleStatus; // 是否可以结算
+
@Column(name = "force_recheck")
@NotNull
private Boolean forceRecheck; // 是否对当前日期强制对账
@@ -149,4 +158,20 @@
public void setRemark(String remark) {
this.remark = remark;
}
+
+ public Boolean getSettleStatus() {
+ return settleStatus;
+ }
+
+ public void setSettleStatus(Boolean settleStatus) {
+ this.settleStatus = settleStatus;
+ }
+
+ public Boolean getRepairStatus() {
+ return repairStatus;
+ }
+
+ public void setRepairStatus(Boolean repairStatus) {
+ this.repairStatus = repairStatus;
+ }
}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionChkdtl.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionChkdtl.java
index f5dc80e..ef5422a 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionChkdtl.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TTransactionChkdtl.java
@@ -3,11 +3,14 @@
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
+import javax.validation.constraints.NotNull;
import java.sql.Timestamp;
@Entity
@Table(name = "TB_TRANSACTION_CHKDTL",
- indexes = {@Index(name = "UK_TRANSACTION_CHKDTL", unique = true, columnList = "ACCDATE,SOURCETYPE,OTHER_REFNO,LOCAL_REFNO")})
+ indexes = {@Index(name = "UK_TRANSACTION_CHKDTL", unique = true, columnList = "ACCDATE,SOURCETYPE,OTHER_REFNO,LOCAL_REFNO"),
+ @Index(name = "TRANSACTION_CHKDTL_FILEID", columnList = "CHKFILE_ID"),
+ @Index(name = "TRANSACTION_CHKDTL_RECORDNO", columnList = "CHKFILE_ID, RECORDNO", unique = true)})
public class TTransactionChkdtl {
@Id
@GenericGenerator(name = "idGenerator", strategy = "uuid")
@@ -16,6 +19,7 @@
private String id;
@Column(name = "CHKFILE_ID", nullable = false, length = 32)
+ @NotNull
private String chkfileId;
@Column(name = "ACCDATE", nullable = false, length = 8)
@@ -25,6 +29,7 @@
private String sourcetype;
@Column(name = "RECORDNO", precision = 9)
+ @NotNull
private Integer recordno;
@Column(name = "AMOUNT", nullable = false, precision = 9, scale = 2)
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_checkfile_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_checkfile_service.kt
index 615db40..46ae340 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_checkfile_service.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/agent/service/citizencard_checkfile_service.kt
@@ -43,21 +43,32 @@
val billDate = checkStatus.checkFileDate
logger.info("大理农商行对账单下载:download checkdate=【$billDate】")
- val chkfile: TTransactionChkfile = transactionReconciliationService.getTransactionChkfile(billDate, checkStatus.sourceType)?.let { file ->
- when (file.status) {
- ConstantUtil.CHKFILE_STATUS_INIT -> file //初始化直接返回
- ConstantUtil.CHKFILE_STATUS_ERROR, ConstantUtil.CHKFILE_STATUS_UNCHECK -> reinitCheckFile(file) //错误或未对账的直接重新拉取
- else -> {
- when (checkStatus.forceRecheck) {
- true -> reinitCheckFile(file) //对账已完成但强制重新对账
- false -> throw TransactionException(-1, "大理农商行该天[$billDate]已对账完成") //对账已完成报错
+ val chkfile: TTransactionChkfile = transactionReconciliationService
+ .getTransactionChkfile(billDate, checkStatus.sourceType)?.let { file ->
+ when (file.status) {
+ ConstantUtil.CHKFILE_STATUS_INIT -> file //初始化直接返回
+ ConstantUtil.CHKFILE_STATUS_ERROR -> reinitCheckFile(file) //错误或未对账的直接重新拉取
+ else -> {
+ when (checkStatus.forceRecheck) {
+ true -> reinitCheckFile(file) //对账已完成但强制重新对账
+ false -> {
+ if (file.status == ConstantUtil.CHKFILE_STATUS_UNCHECK) {
+ file
+ } else {
+ throw TransactionException(-1, "大理农商行该天[$billDate]已对账完成")
+ }
+ } //对账已完成报错
+ }
+ }
}
}
- }
- } ?: transactionReconciliationService.saveInitTransactionChkfile(billDate, checkStatus.sourceType)
+ ?: transactionReconciliationService.saveInitTransactionChkfile(billDate, checkStatus.sourceType)
+ if (chkfile.status == ConstantUtil.CHKFILE_STATUS_UNCHECK) {
+ return successDownload("[$billDate]日对账数据入库成功", checkStatus)
+ }
val resp = citizencardPayService.getChkfilename(billDate, null)
- when(resp.code) {
+ when (resp.code) {
YnrccUtil.CODE_SUCCESS -> {
val agentConfig = sourceTypeService.getChargePaytypeConfig(TradeDict.PAYTYPE_CITIZEN_CARD, true)
val agentUrl = agentConfig[YnrccUtil.YNRCC_ANGENT_URL] + "/download?filename=" + resp.filename
@@ -75,16 +86,10 @@
YnrccUtil.NO_RECORDS_TODAY -> {
//当日无交易明细,也创建空记录
transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "请求银行返回:当日无交易明细")
-
//成功
- return AgentResponse<TSourceTypeCheckStatus>().also {
- it.code = AgentCode.SUCCESS
- it.agentMsg = "当日无交易明细"
- it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
- this.chkfileUrl = "none"
- })
- }
-
+ return successDownload("当日无交易明细", checkStatus.apply {
+ this.chkfileUrl = "none"
+ })
}
else -> {
//报错,退出对账单拉取
@@ -124,18 +129,11 @@
}
override fun downloadCheckFile(checkStatus: TSourceTypeCheckStatus): AgentResponse<TSourceTypeCheckStatus> {
- try{
+ try {
val billDate = checkStatus.checkFileDate
val agentUrl = checkStatus.chkfileUrl
if ("none" == agentUrl) {
- return AgentResponse<TSourceTypeCheckStatus>().also {
- it.code = AgentCode.SUCCESS
- it.agentMsg = "当日无交易明细"
- it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
- this.checkFileOk = true
- this.remark = "当日无交易明细"
- })
- }
+ return successDownload("当日无交易明细", checkStatus)
}
val chkfile = transactionReconciliationService.doInitTransactionChkfile(billDate, checkStatus.sourceType)
@@ -189,14 +187,7 @@
//成功
transactionReconciliationService.doSuccessTransactionChkfile(chkfile, "对账单数据下载成功")
- return AgentResponse<TSourceTypeCheckStatus>().also {
- it.code = AgentCode.SUCCESS
- it.agentMsg = "[$billDate]日对账数据入库成功"
- it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
- this.checkFileOk = true
- this.remark = "[$billDate]日对账数据入库成功"
- })
- }
+ return successDownload("[$billDate]日对账数据入库成功", checkStatus)
}
} else {
logger.error("请求前置download[${chkfile.accdate}]对账单返回失败:httpStatus=[${respClient.status}]。获取对账文件数据失败")
@@ -213,15 +204,26 @@
}
}
- }catch (ex:Exception){
+ } catch (ex: Exception) {
return AgentResponse<TSourceTypeCheckStatus>().also {
it.code = AgentCode.FAIL
- it.agentMsg = ex.message?:"按对账单路径获取对账数据入库异常"
+ it.agentMsg = ex.message ?: "按对账单路径获取对账数据入库异常"
it.payload = checkStatus
}
}
}
+ private fun successDownload(message: String, checkStatus: TSourceTypeCheckStatus): AgentResponse<TSourceTypeCheckStatus> {
+ return AgentResponse<TSourceTypeCheckStatus>().also {
+ it.code = AgentCode.SUCCESS
+ it.agentMsg = message
+ it.payload = sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus.apply {
+ this.checkFileOk = true
+ this.remark = message
+ })
+ }
+ }
+
private fun reinitCheckFile(chkfile: TTransactionChkfile): TTransactionChkfile {
transactionReconciliationService.deleteTransactionChkdtls(chkfile.id) //删除明细
return transactionReconciliationService.saveOrUpdateTransactionChkfile(chkfile.apply {
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 4a552f8..2806109 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
@@ -5,6 +5,7 @@
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.domain.TTransactionChkdtl
import com.supwisdom.dlpay.api.domain.TTransactionChkfile
import com.supwisdom.dlpay.api.service.SourceTypeService
import com.supwisdom.dlpay.api.service.TransactionReconciliationService
@@ -63,7 +64,7 @@
?: newSourceTypeStatus(sourcetype, accdate)
val interval = DateUtil.getIntervalDay(status.checkAccdate, accdate).toInt()
- return if (status.checkStatus && interval == sourcetype.tplusN) {
+ return if (status.checkStatus && status.repairStatus && interval == sourcetype.tplusN) {
// 当前日期对账已完成,并且已经对账到最后一天
null
} else if (status.checkStatus && interval > sourcetype.tplusN) {
@@ -72,8 +73,11 @@
status.checkStatus = false
status.checkFileDate = DateUtil.getNewDay(status.checkFileDate, 1)
status.checkFileOk = false
+ status.repairStatus = false
+ status.settleStatus = false
+ status.forceRecheck = false
sourceTypeService.saveOrUpdateSourceTypeCheckStatus(status)
- } else if (!status.checkStatus && interval >= sourcetype.tplusN) {
+ } else if ((!status.checkStatus || !status.repairStatus) && interval >= sourcetype.tplusN) {
// 当前对账并未完成 且满足对账条件
status
} else {
@@ -150,6 +154,44 @@
}
@Component
+/**
+ * 对账业务主要由三张表组成 TSourceTypeCheckStatus(对账状态表), TTransactionChkfile(对账文件表), TTransactionChkDtl(对账明细表)
+ * 对账状态表有五个状态:
+ * 1. checkFileOk: 表示对账文件是否成功导入对账文件表和对账明细表中
+ * 2. checkStatus: 表示核对流水任务是否完成,当核对任务完成后,同时根据对"对账文件表"的对账结果(result)来判断是否完成核对,只有当 result
+ * 是 "一致" 或 "不平" 时 checkStatus 才为 true
+ * 3. repairStatus: 表示核对完成,并且可以进行补帐交易。对账任务判断 repairStatus == true 之后将会对所有不一致的交易进行补帐。
+ * 4. settleStatus: 表示本次对账完成(核对与补帐都完成),对账任务会在补帐完成后将 settleStatus 置为 true。
+ * 同时 TTransactionChkDtl 的 resovled 状态全部为 'equal'。系统结算任务只有当 settleStatus == true 的时候才会进行
+ * 5. forceRecheck : 表示对当前的 checkDate 的对账文件重新下载并对账
+ *
+ *
+ * 对账文件表的 status 表示本次核对的结果。对账文件表 status 有几种情况:
+ * 1. 初始状态(init),表示对账任务已经触发;
+ * 2. 待核对状态(unchecked), 表示对账文件已存入数据库中;
+ * 3. 错误状态(error), 保存数据有错误
+ * 4. 完成状态(finish) , 表示核对完成状态;
+ *
+ * 对账文件表的对账结果字段(result) 表示本次对账处理结果:
+ * 1. 未核对(none), 表示还未与流水进行比对
+ * 2. 一致( equal), 表示对账完全一致
+ * 3. 不平( notequal), 表示对账不一致,但可以通过补帐交易完成结算
+ * 4. 错误( error ) , 表示核对时数据校验不正确,系统不能通过补帐交易完成结算。需人工审核对账文件后,重新下载对账单并核对。
+ *
+ *
+ * 对账明细表(TTransactionChkDtl) 的 chkstatus 表示核对结果:
+ * 1. 未检查(uncheck), 表示记录还未核对
+ * 2. 一直( equal) ,表示记录核对与流水一致
+ * 3. 交易流水不存在(notexist), 表示本地交易流水不存在,影响对账文件表的 result 状态,为error。
+ * 4. 支付未记账 ( nocharge) ,表示本地流水未成功,需要补帐
+ * 5. 金额不相等(diff), 表示对账明细金额与本地流水不一致, 影响对账文件表的 result 状态,为error。
+ * 6. 记账日期或支付方式错误 (error) , 其它类型错误,影响对账文件表的 result 状态,为error。
+ *
+ * 对账明细表(TTransactionChkDtl) 的 result 表示处理结果
+ * 1. 需要补帐("add")
+ * 2. 一致("equal") , 表示一致
+ * 3. 挂起("hangup"), 表示补帐出现异常
+ */
class SourceTypeCheckExecutor {
companion object {
const val SUCCESS = "success"
@@ -213,7 +255,11 @@
ExecutorResult(checkStatus, FAIL, "下载失败,未能查到对账文件")
}
} else if (acResp.code == AgentCode.SUCCESS) {
- downloadFile(provider, acResp.payload)
+ if (!acResp.payload.checkFileOk) {
+ downloadFile(provider, acResp.payload)
+ } else {
+ ExecutorResult(checkStatus, SUCCESS, acResp.agentMsg)
+ }
} else {
ExecutorResult(checkStatus, FAIL, acResp.agentMsg)
}
@@ -228,51 +274,89 @@
@Async("sourcetypeCheckTaskExecutor")
fun reconciliation(checkStatus: TSourceTypeCheckStatus): Future<ExecutorResult> {
// 3. 完成对账
- val result = transactionReconciliationService.getTransactionChkfile(checkStatus.checkAccdate, checkStatus.sourceType)?.let { chkfile ->
- if (ConstantUtil.CHKFILE_STATUS_UNCHECK == chkfile.status) {
- //TODO:"实现对账逻辑"
- try {
- when (doCheckProcess(chkfile).result) {
- ConstantUtil.CHKFILE_RESULT_EQUAL -> {
- checkStatus.checkStatus = true //对账成功
- sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus)
- ExecutorResult(checkStatus, SUCCESS, "对账成功,双方一致")
+ val result = if (!checkStatus.checkStatus) {
+ transactionReconciliationService
+ .getTransactionChkfile(checkStatus.checkAccdate, checkStatus.sourceType)?.let { chkfile ->
+ try {
+ when (doCheckProcess(chkfile, checkStatus).result) {
+ ConstantUtil.CHKFILE_RESULT_EQUAL -> {
+ ExecutorResult(checkStatus, SUCCESS, "对账成功,双方一致")
+ }
+ else -> ExecutorResult(checkStatus, FAIL, "对账不平")
+ }
+ } catch (ex: Exception) {
+ ex.printStackTrace()
+ ExecutorResult(checkStatus, FAIL, "对账异常")
}
- else -> ExecutorResult(checkStatus, FAIL, "对账不平")
- }
- } catch (ex: Exception) {
- ex.printStackTrace()
- ExecutorResult(checkStatus, FAIL, "对账异常")
- }
- } else {
- ExecutorResult(checkStatus, FAIL, "[${checkStatus.checkAccdate}]日入库的checkfile状态异常[${chkfile.status}]")
- }
- } ?: ExecutorResult(checkStatus, FAIL, "未找到[${checkStatus.checkAccdate}]日入库的对账数据")
+ } ?: ExecutorResult(checkStatus, FAIL, "未找到[${checkStatus.checkAccdate}]日入库的对账数据")
+ } else if (checkStatus.repairStatus) {
+ // 补帐逻辑
+ ExecutorResult(checkStatus, SUCCESS, "对账成功,双方一致")
+ } else if (checkStatus.settleStatus) {
+ ExecutorResult(checkStatus, SUCCESS, "对账完成,可以结算")
+ } else {
+ ExecutorResult(checkStatus, FAIL, "未找到[${checkStatus.checkAccdate}]日入库的对账数据")
+ }
return AsyncResult(result)
}
- fun doCheckProcess(chkfile: TTransactionChkfile): TTransactionChkfile {
- try {
- //统一处理交易一致的记录
- transactionReconciliationService.doCheckEqualTransdtls(chkfile)
- } catch (e1: Exception) {
+ fun doCheckProcess(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TTransactionChkfile {
+ // 假设绝大多数记录都是一致的情况下,下面的逻辑采用每1000条批量处理提高性能
+ //统一处理交易一致的记录, 对账 1
+ (0..chkfile.othercnt step 1000).forEach {
+ try {
+ transactionReconciliationService.doCheckEqualTransdtls(chkfile, it)
+ } catch (e1: Exception) {
+ }
}
-
- //剩余的不平记录逐一对比
+ //剩余的未对账记录逐一对比, 对账 2
+ // 这个规则业务可以处理 "对账1" 中可能漏掉的记录
+ var errorDtl = false
transactionReconciliationService.getUncheckTransactionChkdtls(chkfile.id).forEach {
- transactionReconciliationService.doCheckTransactionChkdtl(it)
+ transactionReconciliationService.doCheckTransactionChkdtl(it).also { chkdtl ->
+ if (chkdtl.chkresult != ConstantUtil.CHKDTL_CHKRESULT_NOCHARGE
+ && chkdtl.chkresult != ConstantUtil.CHKDTL_CHKRESULT_EQUAL) {
+ errorDtl = true
+ }
+ }
}
- //判断chkdtl是否全部对账完成
+ //判断chkdtl是否全部对账完成 , 对账 3
if (transactionReconciliationService.checkUncheckExists(chkfile.id)) {
throw TransactionException(-2, "存在未对账的明细记录")
}
+ //判断本地是否有多余成功流水,并保存 , 对账 4
+ transactionReconciliationService.doCheckLocalMoreDtls(chkfile, checkStatus).also {
+ if (it.isNotEmpty()) {
+ errorDtl = true
+ }
+ }
- //判断本地是否有多余成功流水,并保存
- transactionReconciliationService.doCheckLocalMoreDtls(chkfile)
+ /**
+ * 当我们将每条明细进行核对后,需要明确是否对账完成、是否可以补帐和是否可以结算
+ * 1. 对账完成是指每条对账明细只存在两种状态 CHKDTL_CHKRESULT_EQUAL , CHKDTL_CHKRESULT_NOCHARGE。其它状态都是异常状态
+ * 2. 对"是否可以补帐"的定义是必须"对账完成"
+ * 3. 当所有明细完成对账或补帐才能结算
+ */
+ return if (!errorDtl) {
+ chkfile.status = ConstantUtil.CHKFILE_STATUS_finish
+ checkStatus.checkStatus = true
+ checkStatus.repairStatus = true
+ checkStatus.settleStatus = false
+ // udpate
+ transactionReconciliationService.doConfirmChkfileStatus(chkfile, checkStatus)
+ // 调用补帐业务进行补帐处理
- return transactionReconciliationService.doConfirmChkfileStatus(chkfile)
+ // 完成补帐业务进入结算状态
+ checkStatus.settleStatus = true
+ transactionReconciliationService.doConfirmChkfileStatus(chkfile, checkStatus)
+
+ } else {
+ chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+ checkStatus.settleStatus = false
+ checkStatus.repairStatus = false
+ checkStatus.checkStatus = true
+ transactionReconciliationService.doConfirmChkfileStatus(chkfile, checkStatus)
+ }
}
-
-
}
\ No newline at end of file
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
index be112d1..2e2e89a 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_reconciliation_service_impl.kt
@@ -6,8 +6,10 @@
import com.supwisdom.dlpay.api.dao.TransactionChkdtlDao
import com.supwisdom.dlpay.api.dao.TransactionChkfileDao
import com.supwisdom.dlpay.api.dao.TransactionMainDao
+import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
import com.supwisdom.dlpay.api.domain.TTransactionChkdtl
import com.supwisdom.dlpay.api.domain.TTransactionChkfile
+import com.supwisdom.dlpay.api.service.SourceTypeService
import com.supwisdom.dlpay.api.service.TransactionReconciliationService
import com.supwisdom.dlpay.api.service.TransactionServiceProxy
import com.supwisdom.dlpay.exception.TransactionCheckException
@@ -38,6 +40,9 @@
@Autowired
private lateinit var transactionService: TransactionServiceProxy
+ @Autowired
+ private lateinit var sourceTypeService: SourceTypeService
+
override fun getTransactionChkfile(accdate: String, sourcetype: String): TTransactionChkfile? {
return transactionChkfileDao.getByAccdateAndSourcetype(accdate, sourcetype)
}
@@ -46,7 +51,8 @@
return transactionChkfileDao.getByAccdateAndSourcetype(accdate, sourcetype).let {
if (it != null) {
if (ConstantUtil.CHKFILE_STATUS_INIT != it.status)
- throw TransactionCheckException(TradeErrorCode.BUSINESS_DEAL_ERROR, "accdate=$accdate,sourcetype=$sourcetype 的chkfile已经存在")
+ throw TransactionCheckException(TradeErrorCode.BUSINESS_DEAL_ERROR,
+ "accdate=$accdate,sourcetype=$sourcetype 的chkfile已经存在")
it
} else {
saveInitTransactionChkfile(accdate, sourcetype) //保存init对账文件
@@ -116,61 +122,69 @@
})
}
- override fun doCheckEqualTransdtls(chkfile: TTransactionChkfile) {
+ override fun doCheckEqualTransdtls(chkfile: TTransactionChkfile, start: Int): Int {
//所有一致的记录,一次任务处理
- transactionChkdtlDao.getCheckEqualDetails(chkfile.id)?.forEach { chkdtl ->
- chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_EQUAL
- chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
- chkdtl.remark = "双方交易一致"
- transactionChkdtlDao.save(chkdtl) //对账明细表更新
+ return transactionChkdtlDao.findEqualDtlWithLimit(chkfile.id, start, start + 1000)?.also {
+ it.forEach { chkdtl ->
+ chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_EQUAL
+ chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
+ chkdtl.remark = "双方交易一致"
+ transactionChkdtlDao.save(chkdtl) //对账明细表更新
- transactionService.checkSuccessConfirm(chkdtl.localRefno, chkdtl.otherAccdate)
+ transactionService.checkSuccessConfirm(chkdtl.localRefno, chkdtl.otherAccdate)
+ }
+ }?.size ?: 0
+ }
+
+ override fun getMinChkDtlRecordNo(chkfile: TTransactionChkfile, status: String): Int {
+ return if (status.isEmpty()) {
+ transactionChkdtlDao.getMinRecordNo(chkfile.id)
+ } else {
+ transactionChkdtlDao.getMinRecordNoByStatus(chkfile.id, status)
}
}
override fun getUncheckTransactionChkdtls(chkfileId: String): List<TTransactionChkdtl> {
- return transactionChkdtlDao.getUncheckTransactionChkdtls(chkfileId) ?: ArrayList<TTransactionChkdtl>(0)
+ return transactionChkdtlDao.getUncheckTransactionChkdtls(chkfileId)
+ ?: ArrayList<TTransactionChkdtl>(0)
}
- override fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl) {
+ override fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl): TTransactionChkdtl {
val transMain = transactionMainDao.findByRefno(chkdtl.localRefno)
if (null == transMain || !transMain.person) {
chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_NOTEXIST
chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
chkdtl.remark = "本地流水不存在"
- transactionChkdtlDao.save(chkdtl) //对账明细表更新
+ return transactionChkdtlDao.save(chkdtl) //对账明细表更新
}
- if(!MoneyUtil.moneyEqual(chkdtl.amount,transMain.personDtl.amount)){
+ if (!MoneyUtil.moneyEqual(chkdtl.amount, transMain.personDtl.amount)) {
chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_DIFF
chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
chkdtl.remark = "交易金额不相等"
- transactionChkdtlDao.save(chkdtl) //对账明细表更新
+ return transactionChkdtlDao.save(chkdtl) //对账明细表更新
}
- if(transMain.accdate!=chkdtl.accdate || transMain.sourceType != chkdtl.sourcetype){
- //TODO: 数据异常??
+ if (transMain.accdate != chkdtl.accdate || transMain.sourceType != chkdtl.sourcetype) {
+ // 数据异常, 对账不能完成
chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_ERROR
chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
chkdtl.remark = "记账日期或支付方式错误"
- transactionChkdtlDao.save(chkdtl) //对账明细表更新
+ return transactionChkdtlDao.save(chkdtl) //对账明细表更新
}
- if (transMain.status == TradeDict.DTL_STATUS_SUCCESS) {
+ return if (transMain.status == TradeDict.DTL_STATUS_SUCCESS) {
chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_EQUAL
chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
chkdtl.remark = "双方交易一致"
- transactionChkdtlDao.save(chkdtl) //对账明细表更新
-
transactionService.checkSuccessConfirm(chkdtl.localRefno, chkdtl.otherAccdate)
+ transactionChkdtlDao.save(chkdtl) //对账明细表更新
} else {
- //补账
- transactionService.repair(transMain.refno, chkdtl.otherAccdate, chkdtl.otherRefno, "补账成功")
-
+ //需要补账
chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_NOCHARGE
- chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
- chkdtl.remark = "本地未入账,补账成功"
+ chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_ADD
+ chkdtl.remark = "本地未入账,需要"
transactionChkdtlDao.save(chkdtl) //对账明细表更新
}
}
@@ -179,11 +193,19 @@
return transactionChkdtlDao.getUncheckCount(chkfileId).existed > 0
}
- override fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile) {
- transactionChkdtlDao.findLocalMoreDtls(chkfile.accdate, chkfile.sourcetype, chkfile.id)?.forEach { refno ->
- //保存多余的记录
- transactionMainDao.findByRefno(refno)?.let { transMain ->
- if (TradeDict.DTL_STATUS_SUCCESS == transMain.status && transMain.accdate == chkfile.accdate && transMain.sourceType == chkfile.sourcetype) {
+ private fun updateCheckStatusWhenError(message: String, checkStatus: TSourceTypeCheckStatus,
+ chkfile: TTransactionChkfile) {
+ checkStatus.checkStatus = false
+ checkStatus.remark = message
+ chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+ }
+
+ override fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): List<TTransactionChkdtl> {
+ val result = mutableListOf<TTransactionChkdtl>()
+ transactionChkdtlDao.findLocalMoreDtls(chkfile.accdate, chkfile.sourcetype, chkfile.id)?.also {
+ it.forEach { refno ->
+ //保存多余的记录
+ transactionMainDao.findByRefno(refno)?.also { transMain ->
transactionChkdtlDao.save(TTransactionChkdtl().apply {
this.chkfileId = chkfile.id
this.accdate = chkfile.accdate
@@ -200,30 +222,54 @@
this.otherStatus = "unknown"
this.remark = "本地成功流水第三方流水不存在"
this.extdata = transMain.personDtl.transdesc
- this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_NOTEXIST
+ this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_ERROR
this.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
this.lastsaved = systemUtilService.sysdatetime.sysdate
this.tenantid = chkfile.tenantid
- }) //保存本地多出的记录
+ }).also { dtl ->
+ result.add(dtl)
+ }//保存本地多出的记录
+ }.also { transMain ->
+ if (transMain == null) {
+ chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+ updateCheckStatusWhenError("交易参考号<$refno>异常,未找到Main记录",
+ checkStatus, chkfile)
+ return result
+ }
}
}
+ }?.also {
+ if (it.isNotEmpty()) {
+ chkfile.status = ConstantUtil.CHKFILE_STATUS_ERROR
+ updateCheckStatusWhenError("本地有<${it.size}>条流水不在对账文件中,请联系管理员检查",
+ checkStatus, chkfile)
+ }
}
+ return result
}
- override fun doConfirmChkfileStatus(chkfile: TTransactionChkfile): TTransactionChkfile {
- val personSumInfo = persondtlDao.getPersondtlSumInfo(chkfile.accdate, chkfile.sourcetype, chkfile.tenantid)
- chkfile.localcnt = personSumInfo.totalcnt ?: 0
- chkfile.localamt = personSumInfo.totalamt ?: 0.00
- chkfile.status = ConstantUtil.CHKFILE_STATUS_finish //对账结束
- if (chkfile.othercnt == chkfile.localcnt && MoneyUtil.moneyEqual(chkfile.otheramt, chkfile.localamt)) {
- //fixme: 是否还需判断chkdtl中resolved是否都equal
- chkfile.result = ConstantUtil.CHKFILE_RESULT_EQUAL
- chkfile.remark = "对账完成,双方一致"
- } else {
- chkfile.result = ConstantUtil.CHKFILE_RESULT_UNEQUAL
- chkfile.remark = "对账不平,请查看明细数据"
- }
- transactionChkfileDao.save(chkfile)
- return chkfile
+ override fun doConfirmChkfileStatus(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TTransactionChkfile {
+// if (chkfile.status == ConstantUtil.CHKFILE_STATUS_ERROR) {
+// chkfile.remark = checkStatus.remark
+// } else {
+// val personSumInfo = persondtlDao.getPersondtlSumInfo(chkfile.accdate, chkfile.sourcetype, chkfile.tenantid)
+// chkfile.localcnt = personSumInfo.totalcnt ?: 0
+// chkfile.localamt = personSumInfo.totalamt ?: 0.00
+// chkfile.status = ConstantUtil.CHKFILE_STATUS_finish //对账结束
+// if (chkfile.othercnt == chkfile.localcnt && MoneyUtil.moneyEqual(chkfile.otheramt, chkfile.localamt)) {
+// //fixme: 是否还需判断chkdtl中resolved是否都equal
+// chkfile.result = ConstantUtil.CHKFILE_RESULT_EQUAL
+// chkfile.remark = "对账完成,双方一致"
+// checkStatus.settleStatus = true
+// } else {
+// chkfile.result = ConstantUtil.CHKFILE_RESULT_UNEQUAL
+// chkfile.remark = "对账不平,请查看明细数据"
+// }
+// checkStatus.checkStatus = true
+// checkStatus.remark = "对账完成"
+// }
+
+ sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus)
+ return transactionChkfileDao.save(chkfile)
}
}
\ No newline at end of file
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 c2935c5..9429940 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
@@ -1,6 +1,7 @@
package com.supwisdom.dlpay.api.service
import com.supwisdom.dlpay.api.bean.YnrccChkfileBean
+import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
import com.supwisdom.dlpay.api.domain.TTransactionChkdtl
import com.supwisdom.dlpay.api.domain.TTransactionChkfile
import org.springframework.transaction.annotation.Transactional
@@ -31,21 +32,24 @@
fun saveInitTransactionChkfile(accdate: String, sourcetype: String): TTransactionChkfile
@Transactional(rollbackFor = [Exception::class])
- fun doCheckEqualTransdtls(chkfile: TTransactionChkfile)
+ fun getMinChkDtlRecordNo(chkfile: TTransactionChkfile, status: String): Int
+
+ @Transactional(rollbackFor = [Exception::class])
+ fun doCheckEqualTransdtls(chkfile: TTransactionChkfile, start: Int): Int
@Transactional(rollbackFor = [Exception::class], readOnly = true)
fun getUncheckTransactionChkdtls(chkfileId: String): List<TTransactionChkdtl>
@Transactional(rollbackFor = [Exception::class])
- fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl)
+ fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl): TTransactionChkdtl
@Transactional(rollbackFor = [Exception::class], readOnly = true)
- fun checkUncheckExists(chkfileId: String):Boolean
+ fun checkUncheckExists(chkfileId: String): Boolean
@Transactional(rollbackFor = [Exception::class])
- fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile)
+ fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): List<TTransactionChkdtl>
@Transactional(rollbackFor = [Exception::class])
- fun doConfirmChkfileStatus(chkfile: TTransactionChkfile): TTransactionChkfile
+ fun doConfirmChkfileStatus(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TTransactionChkfile
}
\ No newline at end of file