b368d5aa774868de5010bf5954e66e5cc25bf244
[epayment/food_payapi.git] /
1 package com.supwisdom.dlpay.api.service.impl
2
3 import com.supwisdom.dlpay.agent.citizencard.YnrccUtil
4 import com.supwisdom.dlpay.api.bean.YnrccChkfileBean
5 import com.supwisdom.dlpay.api.dao.PersondtlDao
6 import com.supwisdom.dlpay.api.dao.TransactionChkdtlDao
7 import com.supwisdom.dlpay.api.dao.TransactionChkfileDao
8 import com.supwisdom.dlpay.api.dao.TransactionMainDao
9 import com.supwisdom.dlpay.api.domain.TSourceTypeCheckStatus
10 import com.supwisdom.dlpay.api.domain.TTransactionChkdtl
11 import com.supwisdom.dlpay.api.domain.TTransactionChkfile
12 import com.supwisdom.dlpay.api.service.SourceTypeService
13 import com.supwisdom.dlpay.api.service.TransactionReconciliationService
14 import com.supwisdom.dlpay.api.service.TransactionServiceProxy
15 import com.supwisdom.dlpay.exception.TransactionCheckException
16 import com.supwisdom.dlpay.exception.TransactionException
17 import com.supwisdom.dlpay.framework.dao.BusinessparaDao
18 import com.supwisdom.dlpay.framework.service.SystemUtilService
19 import com.supwisdom.dlpay.framework.tenant.TenantContext
20 import com.supwisdom.dlpay.framework.util.MoneyUtil
21 import com.supwisdom.dlpay.framework.util.TradeDict
22 import com.supwisdom.dlpay.framework.util.TradeErrorCode
23 import com.supwisdom.dlpay.util.ConstantUtil
24 import org.springframework.beans.factory.annotation.Autowired
25 import org.springframework.stereotype.Service
26
27 @Service
28 class TransactionReconciliationServiceImpl : TransactionReconciliationService {
29     @Autowired
30     private lateinit var transactionChkfileDao: TransactionChkfileDao
31     @Autowired
32     private lateinit var transactionChkdtlDao: TransactionChkdtlDao
33     @Autowired
34     private lateinit var systemUtilService: SystemUtilService
35     @Autowired
36     private lateinit var businessparaDao: BusinessparaDao
37     @Autowired
38     private lateinit var transactionMainDao: TransactionMainDao
39     @Autowired
40     private lateinit var persondtlDao: PersondtlDao
41     @Autowired
42     private lateinit var transactionService: TransactionServiceProxy
43
44     @Autowired
45     private lateinit var sourceTypeService: SourceTypeService
46
47     override fun getTransactionChkfile(accdate: String, sourcetype: String): TTransactionChkfile? {
48         return transactionChkfileDao.getByAccdateAndSourcetype(accdate, sourcetype)
49     }
50
51     override fun doInitTransactionChkfile(accdate: String, sourcetype: String): TTransactionChkfile {
52         return transactionChkfileDao.getByAccdateAndSourcetype(accdate, sourcetype).let {
53             if (it != null) {
54                 if (ConstantUtil.CHKFILE_STATUS_INIT != it.status)
55                     throw TransactionCheckException(TradeErrorCode.BUSINESS_DEAL_ERROR,
56                             "accdate=$accdate,sourcetype=$sourcetype 的chkfile已经存在")
57                 it
58             } else {
59                 saveInitTransactionChkfile(accdate, sourcetype) //保存init对账文件
60             }
61         }
62     }
63
64     override fun saveOrUpdateTransactionChkfile(chkfile: TTransactionChkfile): TTransactionChkfile {
65         return transactionChkfileDao.save(chkfile)
66     }
67
68     override fun saveYnrccTransactionChkDtl(chkfile: TTransactionChkfile, bean: YnrccChkfileBean): TTransactionChkdtl {
69         return transactionChkdtlDao.save(TTransactionChkdtl().apply {
70             this.chkfileId = chkfile.id
71             this.accdate = chkfile.accdate
72             this.sourcetype = chkfile.sourcetype
73             this.recordno = bean.recordno
74             this.amount = bean.amount / 100.0
75             this.otherRefno = bean.agentrefno
76             this.localRefno = bean.refno
77             this.otherAccdate = bean.agentdate
78             this.transtype = bean.flag
79             this.otherStatus = bean.status
80             this.remark = null
81             this.extdata = bean.summary
82             this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_UNCHECK
83             this.resolved = ConstantUtil.CHKDTL_RESOLVED_NONE
84             this.lastsaved = systemUtilService.sysdatetime.sysdate
85             this.tenantid = TenantContext.getTenantSchema()
86         })
87     }
88
89     override fun doBatchSaveYnrccTransactionChkDtl(chkfile: TTransactionChkfile, list: ArrayList<YnrccChkfileBean>): Boolean {
90         for (bean in list) {
91             saveYnrccTransactionChkDtl(chkfile, bean)
92         }
93         return true
94     }
95
96     override fun doSuccessTransactionChkfile(chkfile: TTransactionChkfile, remark: String) {
97         val suminfo = transactionChkdtlDao.getTransactionSumInfo(chkfile.accdate, chkfile.sourcetype)
98         chkfile.status = ConstantUtil.CHKFILE_STATUS_UNCHECK
99         chkfile.remark = remark
100         chkfile.othercnt = suminfo.totalcnt ?: 0
101         chkfile.otheramt = suminfo.totalamt ?: 0.0
102         transactionChkfileDao.save(chkfile)
103         businessparaDao.updateBusinessparaValue(YnrccUtil.YNRCC_BILLS_DOWNLOAD_LASTDATE, chkfile.accdate) //更新下载对账单日期
104     }
105
106     override fun deleteTransactionChkdtls(chkfileId: String) {
107         transactionChkdtlDao.deleteByChkfileid(chkfileId)
108     }
109
110     override fun saveInitTransactionChkfile(accdate: String, sourcetype: String): TTransactionChkfile {
111         return transactionChkfileDao.save(TTransactionChkfile().apply {
112             this.accdate = accdate
113             this.sourcetype = sourcetype
114             this.status = ConstantUtil.CHKFILE_STATUS_INIT
115             this.result = ConstantUtil.CHKFILE_RESULT_NONE
116             this.remark = null
117             this.othercnt = 0
118             this.otheramt = 0.00
119             this.localcnt = 0
120             this.localamt = 0.00
121             this.lastsaved = systemUtilService.sysdatetime.sysdate
122             this.tenantid = TenantContext.getTenantSchema()
123         })
124     }
125
126     override fun doCheckEqualTransdtls(chkfile: TTransactionChkfile, start: Int): Int {
127         //所有一致的记录,一次任务处理
128         return transactionChkdtlDao.findEqualDtlWithLimit(chkfile.id, start, start + 1000)?.also {
129             it.forEach { chkdtl ->
130                 chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_EQUAL
131                 chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
132                 chkdtl.remark = "双方交易一致"
133                 transactionChkdtlDao.save(chkdtl) //对账明细表更新
134
135                 transactionService.checkSuccessConfirm(chkdtl.localRefno, chkdtl.otherAccdate)
136             }
137         }?.size ?: 0
138     }
139
140     override fun getMinChkDtlRecordNo(chkfile: TTransactionChkfile, status: String): Int {
141         return if (status.isEmpty()) {
142             transactionChkdtlDao.getMinRecordNo(chkfile.id)
143         } else {
144             transactionChkdtlDao.getMinRecordNoByStatus(chkfile.id, status)
145         }
146     }
147
148     override fun getUncheckTransactionChkdtls(chkfileId: String): List<TTransactionChkdtl> {
149         return transactionChkdtlDao.getUncheckTransactionChkdtls(chkfileId)
150                 ?: ArrayList<TTransactionChkdtl>(0)
151     }
152
153     override fun doCheckTransactionChkdtl(chkdtl: TTransactionChkdtl): TTransactionChkdtl {
154         val transMain = transactionMainDao.findByRefno(chkdtl.localRefno)
155
156         if (null == transMain || !transMain.person) {
157             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_NOTEXIST
158             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
159             chkdtl.remark = "本地流水不存在"
160             return transactionChkdtlDao.save(chkdtl) //对账明细表更新
161         }
162
163         if (!MoneyUtil.moneyEqual(chkdtl.amount, transMain.personDtl.amount)) {
164             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_DIFF
165             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
166             chkdtl.remark = "交易金额不相等"
167             return transactionChkdtlDao.save(chkdtl) //对账明细表更新
168         }
169
170         if (transMain.accdate != chkdtl.accdate || transMain.sourceType != chkdtl.sourcetype) {
171             // 数据异常, 对账不能完成
172             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_ERROR
173             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
174             chkdtl.remark = "记账日期或支付方式错误"
175             return transactionChkdtlDao.save(chkdtl) //对账明细表更新
176         }
177
178         return if (transMain.status == TradeDict.DTL_STATUS_SUCCESS) {
179             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_EQUAL
180             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
181             chkdtl.remark = "双方交易一致"
182             transactionService.checkSuccessConfirm(chkdtl.localRefno, chkdtl.otherAccdate)
183             transactionChkdtlDao.save(chkdtl) //对账明细表更新
184         } else {
185             //需要补账
186             chkdtl.chkresult = ConstantUtil.CHKDTL_CHKRESULT_NOCHARGE
187             chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_ADD
188             chkdtl.remark = "本地未入账,需要补助"
189             transactionChkdtlDao.save(chkdtl) //对账明细表更新
190         }
191     }
192
193     override fun checkUncheckExists(chkfileId: String): Boolean {
194         return transactionChkdtlDao.getCountByChkresult(chkfileId, ConstantUtil.CHKDTL_CHKRESULT_UNCHECK).existed > 0
195     }
196
197     override fun doCheckLocalMoreDtls(chkfile: TTransactionChkfile): Int {
198         return transactionChkdtlDao.findLocalMoreDtls(chkfile.accdate, chkfile.sourcetype, chkfile.id)?.also {
199             it.forEachIndexed { index, refno ->
200                 //保存多余的记录
201                 transactionMainDao.findByRefno(refno)?.also { transMain ->
202                     transactionChkdtlDao.save(TTransactionChkdtl().apply {
203                         this.chkfileId = chkfile.id
204                         this.accdate = chkfile.accdate
205                         this.sourcetype = chkfile.sourcetype
206                         this.recordno = -1 * (index + 1)
207                         this.amount = transMain.personDtl.amount
208                         this.otherRefno = transMain.sourceTypeRefno ?: "$index"
209                         this.localRefno = transMain.refno
210                         this.otherAccdate = transMain.accdate
211                         this.transtype = when (transMain.reverseType) {
212                             "none" -> "pay"
213                             else -> transMain.reverseType
214                         }
215                         this.otherStatus = "unknown"
216                         this.remark = "本地有成功流水,第三方流水不存在"
217                         this.extdata = transMain.personDtl.transdesc
218                         this.chkresult = ConstantUtil.CHKDTL_CHKRESULT_SURPLUS
219                         this.resolved = ConstantUtil.CHKDTL_RESOLVED_HANGUP
220                         this.lastsaved = systemUtilService.sysdatetime.sysdate
221                         this.tenantid = chkfile.tenantid
222                     })//保存本地多出的记录
223                 }.also { transMain ->
224                     if (transMain == null) {
225                         throw TransactionException(-3, "本地存在多余成功流水记录,且交易参考号<$refno>异常,未找到transactionMain记录")
226                     }
227                 }
228             }
229         }?.size ?: 0
230     }
231
232     override fun doFinishChkfileStatus(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus) {
233         if (transactionChkdtlDao.getCountByChkresult(chkfile.id, ConstantUtil.CHKDTL_CHKRESULT_UNCHECK).existed > 0) {
234             throw TransactionException(-2, "存在未对账的明细记录")
235         }
236         //明细已全部校对完
237         chkfile.status = ConstantUtil.CHKFILE_STATUS_FINISH
238         checkStatus.checkStatus = true
239         checkStatus.repairStatus = false
240         checkStatus.settleStatus = false
241         if (transactionChkdtlDao.getErrorCount(chkfile.id).existed > 0) {
242             //除了需要补账的错误外有其他异常
243             chkfile.result = ConstantUtil.CHKFILE_RESULT_ERROR
244             chkfile.remark = "校对完成,明细存在对比异常"
245             transactionChkdtlDao.getCountByChkresult(chkfile.id, ConstantUtil.CHKDTL_CHKRESULT_SURPLUS).existed.also {
246                 if (null != it && it > 0) chkfile.remark = "本地有<$it>条流水不在对账文件中,请联系管理员检查"
247             }
248         } else {
249             //仅存在一致或需补账的记录
250             if (transactionChkdtlDao.getCountByChkresult(chkfile.id, ConstantUtil.CHKDTL_CHKRESULT_NOCHARGE).existed > 0) {
251                 //存在需补账记录
252                 chkfile.result = ConstantUtil.CHKFILE_RESULT_UNEQUAL //不平
253                 chkfile.remark = "校对完成,存在需补账的交易记录"
254             } else {
255                 chkfile.result = ConstantUtil.CHKFILE_RESULT_EQUAL //一致
256                 chkfile.remark = "校对完成,双方交易一致"
257             }
258             checkStatus.repairStatus = true
259         }
260         checkStatus.remark = chkfile.remark
261
262 //        transactionChkfileDao.save(chkfile)
263 //        sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus)
264     }
265
266     override fun getNeedRepairChkdtls(chkfile: TTransactionChkfile): List<TTransactionChkdtl> {
267         return transactionChkdtlDao.getNeedRepairChkdtls(chkfile.id) ?: ArrayList<TTransactionChkdtl>(0)
268     }
269
270     override fun doRepairTransactionChkdtl(chkdtl: TTransactionChkdtl): TTransactionChkdtl {
271         transactionService.repair(chkdtl.localRefno, chkdtl.otherAccdate, chkdtl.otherRefno, "补账成功")
272         chkdtl.resolved = ConstantUtil.CHKDTL_RESOLVED_EQUAL
273         chkdtl.remark = "本地未入账,补账成功"
274         return transactionChkdtlDao.save(chkdtl)
275     }
276
277     override fun saveOrUpdateTransctionChkdtl(chkdtl: TTransactionChkdtl){
278         transactionChkdtlDao.save(chkdtl)
279     }
280
281     override fun doConfirmChkfileStatus(chkfile: TTransactionChkfile, checkStatus: TSourceTypeCheckStatus): TSourceTypeCheckStatus {
282         val personSumInfo = persondtlDao.getPersondtlSumInfo(chkfile.accdate, chkfile.sourcetype, chkfile.tenantid)
283         chkfile.localcnt = personSumInfo.totalcnt ?: 0
284         chkfile.localamt = personSumInfo.totalamt ?: 0.00
285
286         checkStatus.settleStatus = false
287         checkStatus.remark = chkfile.remark
288         if (chkfile.result in setOf(ConstantUtil.CHKFILE_RESULT_EQUAL, ConstantUtil.CHKFILE_RESULT_UNEQUAL)) {
289             val notEqualCount = transactionChkdtlDao.getChkdtlNotEqualCount(chkfile.id).existed
290             val equalCount = transactionChkdtlDao.getChkdtlEqualCount(chkfile.id).existed
291             if (notEqualCount == 0 && equalCount==chkfile.othercnt && chkfile.othercnt == chkfile.localcnt && MoneyUtil.moneyEqual(chkfile.otheramt, chkfile.localamt)) {
292                 //对平
293                 chkfile.remark = when (chkfile.result == ConstantUtil.CHKFILE_RESULT_EQUAL) {
294                     true -> "对账完成,双方交易一致"
295                     else -> "对账完成,补账后双方交易一致"
296                 }
297                 checkStatus.settleStatus = true
298                 checkStatus.forceRecheck = false //清掉
299                 checkStatus.remark = chkfile.remark
300             }else{
301                 checkStatus.remark = "对账完成,补账有失败"
302             }
303         }
304
305         transactionChkfileDao.save(chkfile)
306         return sourceTypeService.saveOrUpdateSourceTypeCheckStatus(checkStatus)
307     }
308 }