对账
diff --git a/build.gradle b/build.gradle
index 0fea5bd..d8703b7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -56,7 +56,7 @@
compile group: 'org.apache.poi', name: 'poi-ooxml', version: '3.10.1'
compile group: 'org.apache.poi', name: 'poi-ooxml-schemas', version: '3.10.1'
compile group: 'org.apache.poi', name: 'poi-scratchpad', version: '3.10.1'
- compile 'com.supwisdom:payapi-sdk:55725ca'
+ compile 'com.supwisdom:payapi-sdk:1.0.9-1-gb3cd8c8'
compile group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: '2.3.4.RELEASE'
@@ -74,6 +74,8 @@
implementation 'org.springframework.session:spring-session-data-redis:2.0.10.RELEASE'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
+ implementation 'net.javacrumbs.shedlock:shedlock-spring:2.5.0'
+ implementation 'net.javacrumbs.shedlock:shedlock-provider-redis-spring:2.5.0'
implementation 'org.postgresql:postgresql:42.2.5'
implementation 'com.jcabi:jcabi-manifests:1.1'
diff --git a/config/application-devel-pg-xkx.properties b/config/application-devel-pg-xkx.properties
new file mode 100644
index 0000000..2b52a50
--- /dev/null
+++ b/config/application-devel-pg-xkx.properties
@@ -0,0 +1,36 @@
+spring.main.banner-mode=off
+# create and drop tables and sequences, loads import.sql
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
+#spring.datasource.continue-on-error=true
+#spring.datasource.initialization-mode=always
+# Postgresql settings
+spring.datasource.platform=postgresql
+spring.datasource.url=jdbc:postgresql://localhost:15432/restaurant
+spring.datasource.username=admin
+spring.datasource.password=123456
+database.dbtype=postgresql
+
+# Redis settings
+spring.redis.host=localhost
+spring.redis.port=16379
+spring.redis.password=kingstar
+spring.redis.database=4
+
+# jwt settings
+jwt.secret=Zj5taLomEbrM0lk+NMQZbHfSxaDU1wekjT+kiC3YzDw=
+jwt.expiration=3600
+
+# user password
+auth.password.bcrypt.seed=
+spring.jackson.serialization.fail-on-empty-beans=false
+
+# task setting
+cron.offlinedtl=0/30 * * * * ?
+payapi.logintime=0 0/20 * * * ?
+#restaurant.chkdtltask.cron=0 0/3 * * * ?
+restaurant.chkdtltask.cron=-
+
+# payapi setting
+payapi.url=http://localhost:8080/payapi
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index dc057cb..bf3de21 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Wed Jul 10 14:15:07 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/src/main/java/com/supwisdom/dlpay/framework/data/CountAmountBean.java b/src/main/java/com/supwisdom/dlpay/framework/data/CountAmountBean.java
new file mode 100644
index 0000000..2fc13b8
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/framework/data/CountAmountBean.java
@@ -0,0 +1,6 @@
+package com.supwisdom.dlpay.framework.data;
+
+public interface CountAmountBean {
+ Integer getTotalcnt();
+ Double getTotalamt();
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckCtlDao.java b/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckCtlDao.java
new file mode 100644
index 0000000..1f80b71
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckCtlDao.java
@@ -0,0 +1,10 @@
+package com.supwisdom.dlpay.restaurant.dao;
+
+import com.supwisdom.dlpay.restaurant.domain.TCheckCtl;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CheckCtlDao extends JpaRepository<TCheckCtl, String> {
+ TCheckCtl getById(String id);
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckDetailDao.java b/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckDetailDao.java
new file mode 100644
index 0000000..83a0ed2
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckDetailDao.java
@@ -0,0 +1,49 @@
+package com.supwisdom.dlpay.restaurant.dao;
+
+import com.supwisdom.dlpay.framework.data.CountAmountBean;
+import com.supwisdom.dlpay.framework.data.ExistBean;
+import com.supwisdom.dlpay.restaurant.domain.TCheckDetail;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface CheckDetailDao extends JpaRepository<TCheckDetail, String> {
+ @Modifying
+ @Query("delete from TCheckDetail where accdate=?1")
+ void deleteCheckDetailByAccdate(String accdate);
+
+ @Query("select count(t.id) as totalcnt,sum(t.amount) as totalamt from TCheckDetail t where t.accdate=?1 ")
+ CountAmountBean getCheckSumInfo(String accdate);
+
+ @Query(value = "select a.* from TB_CHECK_DETAIL a,TB_TRANSDTL b where a.local_refno=b.billno and b.status='success' " +
+ " and a.accdate=b.accdate and a.amount=b.amount and a.shopaccno=b.shopid and a.chkresult <> 'equal' and a.accdate=?1 " +
+ " order by a.local_refno limit ?2 offset ?3 ", nativeQuery = true)
+ List<TCheckDetail> findEqualDtlWithLimit(String accdate, int limit, int offset);
+
+ @Query("from TCheckDetail t where t.accdate=?1 and t.chkresult='uncheck' order by t.localRefno ")
+ List<TCheckDetail> getUnchceckDetails(String accdate);
+
+ @Query("select count(t.id) as existed from TCheckDetail t where t.accdate=?1 and t.chkresult=?2 ")
+ ExistBean getCountByChkresult(String accdate, String chkresult);
+
+ @Query(value = "select t.billno from tb_transdtl t left join TB_CHECK_DETAIL a on t.billno=a.LOCAL_REFNO and a.accdate=:accdate " +
+ " where t.accdate=:accdate and t.status='success' and a.id is null order by t.billno ", nativeQuery = true)
+ List<String> findLocalMoreDtls(@Param("accdate") String accdate);
+
+ @Query("select count(t.id) as existed from TCheckDetail t where t.accdate=?1 and t.chkresult<>'equal' and t.chkresult<>'nocharge' ")
+ ExistBean getErrorCount(String accdate);
+
+ @Query("from TCheckDetail t where t.chkresult='nocharge' and t.resolved<>'equal' and t.accdate=?1 order by t.localRefno ")
+ List<TCheckDetail> getNeedRepairChkdtls(String accdate);
+
+ @Query("select count(t.id) as existed from TCheckDetail t where t.accdate=?1 and t.resolved<>'equal' ")
+ ExistBean getChkdtlNotEqualCount(String accdate);
+
+ @Query("select count(t.id) as existed from TCheckDetail t where t.accdate=?1 and t.resolved='equal' ")
+ ExistBean getChkdtlEqualCount(String accdate);
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckFileDao.java b/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckFileDao.java
new file mode 100644
index 0000000..6a8f211
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/dao/CheckFileDao.java
@@ -0,0 +1,11 @@
+package com.supwisdom.dlpay.restaurant.dao;
+
+import com.supwisdom.dlpay.restaurant.domain.TCheckFile;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface CheckFileDao extends JpaRepository<TCheckFile, String> {
+ TCheckFile getTCheckFileByAccdate(String accdate);
+
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/dao/TransDtlDao.java b/src/main/java/com/supwisdom/dlpay/restaurant/dao/TransDtlDao.java
index ee4f01f..ee2b0e8 100644
--- a/src/main/java/com/supwisdom/dlpay/restaurant/dao/TransDtlDao.java
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/dao/TransDtlDao.java
@@ -1,16 +1,17 @@
package com.supwisdom.dlpay.restaurant.dao;
+import com.supwisdom.dlpay.framework.data.CountAmountBean;
import com.supwisdom.dlpay.restaurant.bean.ManageFeeAmtBean;
import com.supwisdom.dlpay.restaurant.bean.SalesAmtBean;
import com.supwisdom.dlpay.restaurant.domain.TTransDtl;
import com.supwisdom.dlpay.restaurant.domain.TTransDtlFormResult;
import org.springframework.data.domain.Page;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.*;
import org.springframework.stereotype.Repository;
+import javax.persistence.LockModeType;
+import javax.persistence.QueryHint;
import java.util.List;
@Repository
@@ -45,4 +46,19 @@
TTransDtl getByTransdateAndTermidAndTermsqlno(String transdate,Integer termid,Integer termsqlno);
Integer countByAccdateAndCustidAndRuleid(String accdate,String custid,Integer ruleid);
+
+ @Query("select min(accdate) from TTransDtl")
+ String getMinTransdate();
+
+ @Modifying
+ @Query(value = "update TB_TRANSDTL set core_accdate=?2,core_sourcetype=?3,core_status='success' where billno=?1 ", nativeQuery = true)
+ void updateSuccessTransdtlAfterCheck(String billno, String accdate, String sourcetype);
+
+ @Lock(LockModeType.PESSIMISTIC_WRITE)
+ @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "0")})
+ @Query("from TTransDtl t where t.billno=?1 ")
+ TTransDtl getByBillnoWithLock(String billno);
+
+ @Query("select count(t.billno) as totalcnt,sum(t.amount) as totalamt from TTransDtl t where t.status='success' and t.accdate=?1 ")
+ CountAmountBean getLocalTransdtlSumInfo(String accdate);
}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckCtl.java b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckCtl.java
new file mode 100644
index 0000000..8a94d6e
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckCtl.java
@@ -0,0 +1,87 @@
+package com.supwisdom.dlpay.restaurant.domain;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+@Entity
+@Table(name = "TB_CHECK_CTL")
+public class TCheckCtl {
+ @Id
+ @Column(name = "ID", nullable = false, length = 32)
+ private String id;
+
+ @Column(name = "STARTDATE", length = 8)
+ private String startdate;
+
+ @Column(name = "CHECKDATE", nullable = false, length = 8)
+ private String checkdate; //对账日期
+
+ @Column(name = "DOWNLOAD_OK", nullable = false)
+ private Boolean downloadOk;
+
+ @Column(name = "CHECK_OK", nullable = false)
+ private Boolean checkOk;
+
+ @Column(name = "REMARK", length = 1000)
+ private String remark;
+
+ @Version
+ @Column(name = "LASTSAVED")
+ private Timestamp lastsaved;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getStartdate() {
+ return startdate;
+ }
+
+ public void setStartdate(String startdate) {
+ this.startdate = startdate;
+ }
+
+ public String getCheckdate() {
+ return checkdate;
+ }
+
+ public void setCheckdate(String checkdate) {
+ this.checkdate = checkdate;
+ }
+
+ public Boolean getDownloadOk() {
+ return downloadOk;
+ }
+
+ public void setDownloadOk(Boolean downloadOk) {
+ this.downloadOk = downloadOk;
+ }
+
+ public Boolean getCheckOk() {
+ return checkOk;
+ }
+
+ public void setCheckOk(Boolean checkOk) {
+ this.checkOk = checkOk;
+ }
+
+ public String getRemark() {
+ return remark;
+ }
+
+ public void setRemark(String remark) {
+ this.remark = remark;
+ }
+
+ public Timestamp getLastsaved() {
+ return lastsaved;
+ }
+
+ public void setLastsaved(Timestamp lastsaved) {
+ this.lastsaved = lastsaved;
+ }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckDetail.java b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckDetail.java
new file mode 100644
index 0000000..33c9e78
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckDetail.java
@@ -0,0 +1,148 @@
+package com.supwisdom.dlpay.restaurant.domain;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+@Entity
+@Table(name = "TB_CHECK_DETAIL",
+ indexes = {@Index(name = "IDX_CHECK_DETAIL_ACCDATE", columnList = "ACCDATE"),
+ @Index(name = "UK_CHECK_DETAIL", unique = true, columnList = "ACCDATE,SHOPACCNO,LOCAL_REFNO")})
+public class TCheckDetail {
+ @Id
+ @GenericGenerator(name = "ckidGenerator", strategy = "uuid")
+ @GeneratedValue(generator = "ckidGenerator")
+ @Column(name = "ID", nullable = false, length = 32)
+ private String id;
+
+ @Column(name = "ACCDATE", nullable = false, length = 8)
+ private String accdate;
+
+ @Column(name = "SHOPACCNO", nullable = false, length = 32)
+ private String shopaccno;
+
+ @Column(name = "OTHER_REFNO", nullable = false, length = 32)
+ private String otherRefno; //第三方流水号
+
+ @Column(name = "LOCAL_REFNO", nullable = false, length = 32)
+ private String localRefno; //本地流水号
+
+ @Column(name = "AMOUNT", nullable = false, precision = 9, scale = 2)
+ private Double amount;
+
+ @Column(name = "OTHER_SOURCETYPE", nullable = false, length = 32)
+ private String otherSourcetype; //第三方支付方式
+
+ @Column(name = "PAYTIME", length = 32)
+ private String paytime;
+
+ @Column(name = "CHKRESULT", length = 20)
+ private String chkresult;
+
+ @Column(name = "RESOLVED", length = 20)
+ private String resolved;
+
+ @Column(name = "REMARK", length = 200)
+ private String remark;
+
+ @Version
+ @Column(name = "LASTSAVED")
+ private Timestamp lastsaved;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getAccdate() {
+ return accdate;
+ }
+
+ public void setAccdate(String accdate) {
+ this.accdate = accdate;
+ }
+
+ public String getShopaccno() {
+ return shopaccno;
+ }
+
+ public void setShopaccno(String shopaccno) {
+ this.shopaccno = shopaccno;
+ }
+
+ public String getOtherRefno() {
+ return otherRefno;
+ }
+
+ public void setOtherRefno(String otherRefno) {
+ this.otherRefno = otherRefno;
+ }
+
+ public String getLocalRefno() {
+ return localRefno;
+ }
+
+ public void setLocalRefno(String localRefno) {
+ this.localRefno = localRefno;
+ }
+
+ public Double getAmount() {
+ return amount;
+ }
+
+ public void setAmount(Double amount) {
+ this.amount = amount;
+ }
+
+ public String getOtherSourcetype() {
+ return otherSourcetype;
+ }
+
+ public void setOtherSourcetype(String otherSourcetype) {
+ this.otherSourcetype = otherSourcetype;
+ }
+
+ public String getPaytime() {
+ return paytime;
+ }
+
+ public void setPaytime(String paytime) {
+ this.paytime = paytime;
+ }
+
+ public String getChkresult() {
+ return chkresult;
+ }
+
+ public void setChkresult(String chkresult) {
+ this.chkresult = chkresult;
+ }
+
+ public String getResolved() {
+ return resolved;
+ }
+
+ public void setResolved(String resolved) {
+ this.resolved = resolved;
+ }
+
+ public String getRemark() {
+ return remark;
+ }
+
+ public void setRemark(String remark) {
+ this.remark = remark;
+ }
+
+ public Timestamp getLastsaved() {
+ return lastsaved;
+ }
+
+ public void setLastsaved(Timestamp lastsaved) {
+ this.lastsaved = lastsaved;
+ }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckFile.java b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckFile.java
new file mode 100644
index 0000000..28b94bc
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TCheckFile.java
@@ -0,0 +1,109 @@
+package com.supwisdom.dlpay.restaurant.domain;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+@Entity
+@Table(name = "TB_CHECK_FILE")
+public class TCheckFile {
+ @Id
+ @Column(name = "ACCDATE", nullable = false, length = 8)
+ private String accdate;
+
+ @Column(name = "STATUS", nullable = false, length = 20)
+ private String status;
+
+ @Column(name = "CHKRESULT", nullable = false, length = 20)
+ private String chkresult;
+
+ @Column(name = "OTHERCNT", nullable = false, precision = 9)
+ private Integer othercnt;
+
+ @Column(name = "OTHERAMT", nullable = false, precision = 15, scale = 2)
+ private Double otheramt;
+
+ @Column(name = "LOCALCNT", nullable = false, precision = 9)
+ private Integer localcnt;
+
+ @Column(name = "LOCALAMT", nullable = false, precision = 15, scale = 2)
+ private Double localamt;
+
+ @Column(name = "REMARK", length = 1000)
+ private String remark;
+
+ @Version
+ @Column(name = "LASTSAVED")
+ private Timestamp lastsaved;
+
+ public String getAccdate() {
+ return accdate;
+ }
+
+ public void setAccdate(String accdate) {
+ this.accdate = accdate;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getChkresult() {
+ return chkresult;
+ }
+
+ public void setChkresult(String chkresult) {
+ this.chkresult = chkresult;
+ }
+
+ public Integer getOthercnt() {
+ return othercnt;
+ }
+
+ public void setOthercnt(Integer othercnt) {
+ this.othercnt = othercnt;
+ }
+
+ public Double getOtheramt() {
+ return otheramt;
+ }
+
+ public void setOtheramt(Double otheramt) {
+ this.otheramt = otheramt;
+ }
+
+ public Integer getLocalcnt() {
+ return localcnt;
+ }
+
+ public void setLocalcnt(Integer localcnt) {
+ this.localcnt = localcnt;
+ }
+
+ public Double getLocalamt() {
+ return localamt;
+ }
+
+ public void setLocalamt(Double localamt) {
+ this.localamt = localamt;
+ }
+
+ public String getRemark() {
+ return remark;
+ }
+
+ public void setRemark(String remark) {
+ this.remark = remark;
+ }
+
+ public Timestamp getLastsaved() {
+ return lastsaved;
+ }
+
+ public void setLastsaved(Timestamp lastsaved) {
+ this.lastsaved = lastsaved;
+ }
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TTransDtl.java b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TTransDtl.java
index bda2bea..864716a 100644
--- a/src/main/java/com/supwisdom/dlpay/restaurant/domain/TTransDtl.java
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/domain/TTransDtl.java
@@ -37,6 +37,7 @@
private String coreAccdate;
private String coreStatus;
private String managefeetype;
+ private String coreSourcetype; //核心平台支付方式
@Id
@@ -280,4 +281,13 @@
public void setManagefeetype(String managefeetype) {
this.managefeetype = managefeetype;
}
+
+ @Column(name = "CORE_SOURCETYPE", length = 32)
+ public String getCoreSourcetype() {
+ return coreSourcetype;
+ }
+
+ public void setCoreSourcetype(String coreSourcetype) {
+ this.coreSourcetype = coreSourcetype;
+ }
}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/service/CheckTransdtlService.java b/src/main/java/com/supwisdom/dlpay/restaurant/service/CheckTransdtlService.java
new file mode 100644
index 0000000..a488d67
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/service/CheckTransdtlService.java
@@ -0,0 +1,68 @@
+package com.supwisdom.dlpay.restaurant.service;
+
+import com.supwisdom.dlpay.framework.domain.TShopSettlement;
+import com.supwisdom.dlpay.restaurant.domain.TCheckCtl;
+import com.supwisdom.dlpay.restaurant.domain.TCheckDetail;
+import com.supwisdom.dlpay.restaurant.domain.TCheckFile;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+public interface CheckTransdtlService {
+ @Transactional(rollbackFor = Exception.class)
+ TCheckCtl doGetCheckTransdtlCtl();
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckCtl doSwitchCheckCtlChkdate(TCheckCtl ctl);
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckCtl doUpdateCheckCtl(TCheckCtl ctl);
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ List<TShopSettlement> getAllSettlementShops();
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckFile doGetCheckFileRecordByAccdate(String accdate);
+
+ @Transactional(rollbackFor = Exception.class)
+ boolean saveCheckDetails(String checkdate, String shopaccno, String txt) throws Exception;
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckCtl doFinishDownloadData(TCheckFile checkFile, TCheckCtl checkCtl);
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ TCheckFile getCheckFileByAccdate(String accdate);
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckFile doUpdateCheckFile(TCheckFile checkFile);
+
+ @Transactional(rollbackFor = Exception.class)
+ void doCheckEqualTransdtls(String accdate, int offset, int limit);
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ List<TCheckDetail> getUncheckDtlsByAccdate(String accdate);
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckDetail doUpdateCheckDetail(TCheckDetail detail);
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckDetail doCheckSingleTransdtl(TCheckDetail detail);
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ boolean checkUncheckExists(String accdate);
+
+ @Transactional(rollbackFor = Exception.class)
+ int doCheckLocalMoreDtls(String accdate);
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ boolean checkFileHaveErrors(String accdate);
+
+ @Transactional(rollbackFor = Exception.class, readOnly = true)
+ List<TCheckDetail> getNeedRepairChkdtls(String accdate);
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckDetail doRepairCheckDetail(TCheckDetail detail);
+
+ @Transactional(rollbackFor = Exception.class)
+ TCheckCtl doConfirmCheckFile(TCheckCtl ctl, TCheckFile checkFile);
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/CheckTransdtlServiceImpl.java b/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/CheckTransdtlServiceImpl.java
new file mode 100644
index 0000000..551fdcb
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/service/impl/CheckTransdtlServiceImpl.java
@@ -0,0 +1,348 @@
+package com.supwisdom.dlpay.restaurant.service.impl;
+
+import com.supwisdom.dlpay.api.bean.DownloadShopBillData;
+import com.supwisdom.dlpay.framework.dao.ShopSettlementDao;
+import com.supwisdom.dlpay.framework.data.CountAmountBean;
+import com.supwisdom.dlpay.framework.data.ExistBean;
+import com.supwisdom.dlpay.framework.domain.TShopSettlement;
+import com.supwisdom.dlpay.framework.service.SystemUtilService;
+import com.supwisdom.dlpay.framework.util.DateUtil;
+import com.supwisdom.dlpay.framework.util.MoneyUtil;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.framework.util.TradeDict;
+import com.supwisdom.dlpay.restaurant.dao.CheckCtlDao;
+import com.supwisdom.dlpay.restaurant.dao.CheckDetailDao;
+import com.supwisdom.dlpay.restaurant.dao.CheckFileDao;
+import com.supwisdom.dlpay.restaurant.dao.TransDtlDao;
+import com.supwisdom.dlpay.restaurant.domain.TCheckCtl;
+import com.supwisdom.dlpay.restaurant.domain.TCheckDetail;
+import com.supwisdom.dlpay.restaurant.domain.TCheckFile;
+import com.supwisdom.dlpay.restaurant.domain.TTransDtl;
+import com.supwisdom.dlpay.restaurant.service.CheckTransdtlService;
+import com.supwisdom.dlpay.restaurant.util.RestaurantConstant;
+import org.apache.commons.beanutils.BeanUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class CheckTransdtlServiceImpl implements CheckTransdtlService {
+ @Autowired
+ private CheckCtlDao checkCtlDao;
+ @Autowired
+ private ShopSettlementDao shopSettlementDao;
+ @Autowired
+ private TransDtlDao transDtlDao;
+ @Autowired
+ private CheckFileDao checkFileDao;
+ @Autowired
+ private CheckDetailDao checkDetailDao;
+ @Autowired
+ private SystemUtilService systemUtilService;
+
+ private static final Logger logger = LoggerFactory.getLogger(CheckTransdtlServiceImpl.class);
+
+ @Override
+ public TCheckCtl doGetCheckTransdtlCtl() {
+ TCheckCtl ctl = checkCtlDao.getById(RestaurantConstant.CHECK_TRANSDTL_CTLID);
+ if (null != ctl) return ctl;
+
+ //第一次新增
+ ctl = new TCheckCtl();
+ ctl.setId(RestaurantConstant.CHECK_TRANSDTL_CTLID);
+ ctl.setStartdate(getMinTransdate()); //有流水的最小日期
+ ctl.setCheckdate(ctl.getStartdate());
+ ctl.setDownloadOk(false);
+ ctl.setCheckOk(false);
+ ctl.setRemark(null);
+ return checkCtlDao.save(ctl);
+ }
+
+ private String getMinTransdate() {
+ String date = transDtlDao.getMinTransdate();
+ if (null != date) return date;
+ return systemUtilService.getSysdatetime().getHostdate();
+ }
+
+ @Override
+ public TCheckCtl doSwitchCheckCtlChkdate(TCheckCtl ctl) {
+ ctl.setCheckdate(DateUtil.getNewDay(ctl.getCheckdate(), 1));
+ ctl.setDownloadOk(false);
+ ctl.setCheckOk(false);
+ ctl.setRemark(null);
+ return checkCtlDao.save(ctl);
+ }
+
+ @Override
+ public TCheckCtl doUpdateCheckCtl(TCheckCtl ctl) {
+ return checkCtlDao.save(ctl);
+ }
+
+ @Override
+ public List<TShopSettlement> getAllSettlementShops() {
+ List<TShopSettlement> list = shopSettlementDao.findAll();
+ if (null != list) return list;
+ return new ArrayList<>(0);
+ }
+
+ @Override
+ public TCheckFile doGetCheckFileRecordByAccdate(String accdate) {
+ checkDetailDao.deleteCheckDetailByAccdate(accdate);
+ TCheckFile checkFile = checkFileDao.getTCheckFileByAccdate(accdate);
+ if (null == checkFile) {
+ checkFile = new TCheckFile();
+ checkFile.setAccdate(accdate);
+ }
+ //清零
+ checkFile.setStatus(RestaurantConstant.CHKFILE_STATUS_INIT);
+ checkFile.setChkresult(RestaurantConstant.CHKFILE_CHKRESULT_NONE);
+ checkFile.setOthercnt(0);
+ checkFile.setOtheramt(0D);
+ checkFile.setLocalcnt(0);
+ checkFile.setLocalamt(0D);
+ checkFile.setRemark(null);
+ return checkFileDao.save(checkFile);
+ }
+
+ private DownloadShopBillData populate(String[] fields, String[] columns) {
+ if (fields.length != columns.length) throw new IllegalArgumentException("错误的列定义");
+ DownloadShopBillData bean = new DownloadShopBillData();
+ Map<String, String> data = new HashMap<>(0);
+ for (int col = 0; col < columns.length; ++col) {
+ data.put(fields[col], columns[col]);
+ }
+ try {
+ BeanUtils.populate(bean, data);
+ return bean;
+ } catch (Exception e) {
+ throw new IllegalArgumentException("交易明细转换错误");
+ }
+ }
+
+ @Override
+ public boolean saveCheckDetails(String checkdate, String shopaccno, String txt) throws Exception {
+ InputStream is = new ByteArrayInputStream(txt.getBytes("UTF-8"));
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+
+ String header = reader.readLine();
+ String[] fields = header.split("\\|", 0); //第一行头数据 (limit=0去掉末尾空串)
+
+ List<TCheckDetail> list = new ArrayList<>(0);
+ while (true) {
+ String line = reader.readLine();
+ if (StringUtil.isEmpty(line)) break; //空行为结束
+ String[] columns = line.split("\\|", 0);
+ DownloadShopBillData bean = populate(fields, columns);
+ TCheckDetail detail = new TCheckDetail();
+ detail.setAccdate(checkdate);
+ detail.setShopaccno(shopaccno);
+ detail.setOtherRefno(bean.getRefno());
+ detail.setLocalRefno(bean.getBillno());
+ detail.setAmount(bean.getAmount() / 100.0);
+ detail.setOtherSourcetype(bean.getSourcetype());
+ detail.setPaytime(bean.getPaytime());
+ detail.setChkresult(RestaurantConstant.CHKDTL_CHKRESULT_UNCHECK);
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_NONE);
+ list.add(detail);
+ }
+ reader.close();
+ is.close();
+
+ checkDetailDao.saveAll(list); //批量保存
+ return true;
+ }
+
+ @Override
+ public TCheckCtl doFinishDownloadData(TCheckFile checkFile, TCheckCtl checkCtl) {
+ CountAmountBean suminfo = checkDetailDao.getCheckSumInfo(checkFile.getAccdate());
+ checkFile.setOthercnt(suminfo.getTotalcnt() == null ? 0 : suminfo.getTotalcnt());
+ checkFile.setOtheramt(suminfo.getTotalamt() == null ? 0D : suminfo.getTotalamt());
+ checkFile.setStatus(RestaurantConstant.CHKFILE_STATUS_UNCHECK);
+ checkFile.setRemark("下载对账单数据成功!");
+ checkFileDao.save(checkFile);
+
+ checkCtl.setDownloadOk(true);
+ checkCtl.setRemark("下载对账单数据成功!");
+ return checkCtlDao.save(checkCtl);
+ }
+
+ @Override
+ public TCheckFile getCheckFileByAccdate(String accdate) {
+ if (StringUtil.isEmpty(accdate)) return null;
+ return checkFileDao.getTCheckFileByAccdate(accdate.trim());
+ }
+
+ @Override
+ public TCheckFile doUpdateCheckFile(TCheckFile checkFile){
+ return checkFileDao.save(checkFile);
+ }
+
+ @Override
+ public void doCheckEqualTransdtls(String accdate, int offset, int limit) {
+ List<TCheckDetail> detailList = checkDetailDao.findEqualDtlWithLimit(accdate, limit, offset);
+ if (StringUtil.isEmpty(detailList)) return;
+ for (TCheckDetail detail : detailList) {
+ detail.setChkresult(RestaurantConstant.CHKDTL_CHKRESULT_EQUAL);
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_EQUAL);
+ detail.setRemark("双方交易一致");
+ checkDetailDao.save(detail);
+ transDtlDao.updateSuccessTransdtlAfterCheck(detail.getLocalRefno(), accdate, detail.getOtherSourcetype());
+ }
+ }
+
+ @Override
+ public List<TCheckDetail> getUncheckDtlsByAccdate(String accdate) {
+ List<TCheckDetail> list = checkDetailDao.getUnchceckDetails(accdate);
+ if (null != list) return list;
+ return new ArrayList<>(0);
+ }
+
+ @Override
+ public TCheckDetail doUpdateCheckDetail(TCheckDetail detail) {
+ return checkDetailDao.save(detail);
+ }
+
+ @Override
+ public TCheckDetail doCheckSingleTransdtl(TCheckDetail detail) {
+ TTransDtl transDtl = transDtlDao.getByBillnoWithLock(detail.getLocalRefno());
+
+ if (null == transDtl) {
+ detail.setChkresult(RestaurantConstant.CHKDTL_CHKRESULT_NOTEXIST);
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_HANGUP);
+ detail.setRemark("本地流水不存在");
+ return checkDetailDao.save(detail);
+ }
+
+ if (!MoneyUtil.moneyEqual(detail.getAmount(), transDtl.getAmount())) {
+ detail.setChkresult(RestaurantConstant.CHKDTL_CHKRESULT_DIFF);
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_HANGUP);
+ detail.setRemark("交易金额不相等");
+ return checkDetailDao.save(detail);
+ }
+
+ if (!detail.getAccdate().equals(transDtl.getAccdate()) || !detail.getShopaccno().equals(transDtl.getShopid())) {
+ detail.setChkresult(RestaurantConstant.CHKDTL_CHKRESULT_ERROR);
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_HANGUP);
+ detail.setRemark("记账日期或结算商户错误");
+ return checkDetailDao.save(detail);
+ }
+
+ if (TradeDict.DTL_STATUS_SUCCESS.equals(transDtl.getStatus())) {
+ transDtl.setCoreStatus(TradeDict.DTL_STATUS_SUCCESS);
+ transDtl.setCoreAccdate(detail.getAccdate());
+ transDtl.setCoreSourcetype(detail.getOtherSourcetype());
+ transDtlDao.save(transDtl);
+
+ detail.setChkresult(RestaurantConstant.CHKDTL_CHKRESULT_EQUAL);
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_EQUAL);
+ detail.setRemark("双方交易一致");
+ return checkDetailDao.save(detail);
+ } else {
+ detail.setChkresult(RestaurantConstant.CHKDTL_CHKRESULT_NOCHARGE);
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_ADD);
+ detail.setRemark("本地未入账,需要补帐");
+ return checkDetailDao.save(detail);
+ }
+ }
+
+ @Override
+ public boolean checkUncheckExists(String accdate) {
+ ExistBean existBean = checkDetailDao.getCountByChkresult(accdate, RestaurantConstant.CHKDTL_CHKRESULT_UNCHECK);
+ if (null != existBean && existBean.getExisted() > 0) return true;
+ return false;
+ }
+
+ @Override
+ public int doCheckLocalMoreDtls(String accdate) {
+ List<String> moreBillnos = checkDetailDao.findLocalMoreDtls(accdate);
+ if (StringUtil.isEmpty(moreBillnos)) return 0; //本地无多余成功流水
+ for (String billno : moreBillnos) {
+ TTransDtl transDtl = transDtlDao.getByBillno(billno);
+ TCheckDetail detail = new TCheckDetail();
+ detail.setAccdate(accdate);
+ detail.setShopaccno(transDtl.getShopid());
+ detail.setOtherRefno(transDtl.getRefno());
+ detail.setLocalRefno(transDtl.getBillno());
+ detail.setAmount(transDtl.getAmount());
+ detail.setOtherSourcetype("unknow");
+ detail.setPaytime(transDtl.getAccdate() + transDtl.getAcctime());
+ detail.setChkresult(RestaurantConstant.CHKDTL_CHKRESULT_SURPLUS); //本地多余流水
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_HANGUP); //挂起
+ detail.setRemark("本地有成功流水,核心平台对账流水不存在");
+ checkDetailDao.save(detail);
+ }
+ return moreBillnos.size();
+ }
+
+ @Override
+ public boolean checkFileHaveErrors(String accdate) {
+ ExistBean existBean = checkDetailDao.getErrorCount(accdate);
+ if (null != existBean && existBean.getExisted() > 0) return true;
+ return false;
+ }
+
+ @Override
+ public List<TCheckDetail> getNeedRepairChkdtls(String accdate) {
+ List<TCheckDetail> list = checkDetailDao.getNeedRepairChkdtls(accdate);
+ if (null != list) return list;
+ return new ArrayList<>(0);
+ }
+
+ @Override
+ public TCheckDetail doRepairCheckDetail(TCheckDetail detail) {
+ TTransDtl transDtl = transDtlDao.getByBillnoWithLock(detail.getLocalRefno());
+ if (!RestaurantConstant.STATUS_TRANSDTL_SUCCESS.equals(transDtl.getStatus())) {
+ transDtl.setAccdate(detail.getAccdate());
+ transDtl.setAcctime(systemUtilService.getSysdatetime().getHosttime());
+ transDtl.setStatus(RestaurantConstant.STATUS_TRANSDTL_SUCCESS); //流水置为成功
+ transDtl.setRefno(detail.getOtherRefno());
+ }
+ transDtl.setCoreAccdate(detail.getAccdate());
+ transDtl.setCoreSourcetype(detail.getOtherSourcetype());
+ transDtl.setCoreStatus(RestaurantConstant.STATUS_TRANSDTL_SUCCESS);
+ transDtlDao.save(transDtl);
+
+ detail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_EQUAL); //补账成功
+ detail.setRemark("本地未入账,补账成功");
+ return checkDetailDao.save(detail);
+ }
+
+ @Override
+ public TCheckCtl doConfirmCheckFile(TCheckCtl ctl, TCheckFile checkFile) {
+ CountAmountBean localSuminfo = transDtlDao.getLocalTransdtlSumInfo(checkFile.getAccdate());
+ checkFile.setLocalcnt(localSuminfo.getTotalcnt() == null ? 0 : localSuminfo.getTotalcnt());
+ checkFile.setLocalamt(localSuminfo.getTotalamt() == null ? 0D : localSuminfo.getTotalamt());
+
+ int notEqualCount = checkDetailDao.getChkdtlNotEqualCount(checkFile.getAccdate()).getExisted();
+ int equalCount = checkDetailDao.getChkdtlEqualCount(checkFile.getAccdate()).getExisted();
+ if (notEqualCount == 0 && equalCount == checkFile.getOthercnt() && equalCount == checkFile.getLocalcnt() && MoneyUtil.moneyEqual(checkFile.getOtheramt(), checkFile.getLocalamt())) {
+ //对平
+ checkFile.setStatus(RestaurantConstant.CHKFILE_STATUS_FINISH);
+ checkFile.setChkresult(RestaurantConstant.CHKFILE_CHKRESULT_SUCCESS);
+ checkFile.setRemark("对账完成,双方交易一致");
+ ctl.setCheckOk(true);
+ ctl.setRemark(checkFile.getRemark());
+ logger.info("accdate=[" + checkFile.getAccdate() + "]对账完成,双方交易一致");
+ } else {
+ checkFile.setStatus(RestaurantConstant.CHKFILE_STATUS_FINISH);
+ checkFile.setChkresult(RestaurantConstant.CHKFILE_CHKRESULT_FAIL);
+ checkFile.setRemark("对账完成,补账有失败导致不平");
+ ctl.setCheckOk(false);
+ ctl.setRemark(checkFile.getRemark());
+ logger.error("accdate=[" + checkFile.getAccdate() + "]对账完成,补账有失败导致不平!!!");
+ }
+ checkFileDao.save(checkFile);
+ return checkCtlDao.save(ctl);
+ }
+
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/task/CheckTransdtlTask.java b/src/main/java/com/supwisdom/dlpay/restaurant/task/CheckTransdtlTask.java
new file mode 100644
index 0000000..0e37ba0
--- /dev/null
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/task/CheckTransdtlTask.java
@@ -0,0 +1,232 @@
+package com.supwisdom.dlpay.restaurant.task;
+
+import com.google.gson.Gson;
+import com.supwisdom.dlpay.api.bean.ApiResponse;
+import com.supwisdom.dlpay.api.bean.DownloadShopBillParam;
+import com.supwisdom.dlpay.framework.domain.TShopSettlement;
+import com.supwisdom.dlpay.framework.service.SystemUtilService;
+import com.supwisdom.dlpay.framework.util.DateUtil;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.paysdk.proxy.ShopProxy;
+import com.supwisdom.dlpay.restaurant.domain.TCheckCtl;
+import com.supwisdom.dlpay.restaurant.domain.TCheckDetail;
+import com.supwisdom.dlpay.restaurant.domain.TCheckFile;
+import com.supwisdom.dlpay.restaurant.service.CheckTransdtlService;
+import com.supwisdom.dlpay.restaurant.util.RestaurantConstant;
+import net.javacrumbs.shedlock.core.SchedulerLock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+@Component
+@EnableScheduling
+public class CheckTransdtlTask {
+ @Autowired
+ private SystemUtilService systemUtilService;
+ @Autowired
+ private CheckTransdtlService checkTransdtlService;
+
+ @Autowired
+ private ShopProxy shopProxy;
+
+
+ private static final Logger logger = LoggerFactory.getLogger(CheckTransdtlTask.class);
+
+
+ @Scheduled(cron = "${restaurant.chkdtltask.cron}")
+ @SchedulerLock(name = "RestaurantCheckTransdtlTask", lockAtMostForString = "PT20M")
+ public void doCheckTransdtlTask(){
+ try{
+ long t1 = System.currentTimeMillis();
+ logger.info("=================== 对账任务开始: ===================");
+ //step1: 获取上次对账到哪一天
+ TCheckCtl chkCtl = checkTransdtlService.doGetCheckTransdtlCtl();
+ if (null == chkCtl) {
+ logger.error("对账状态表为空!!!");
+ return;
+ }
+
+ if (chkCtl.getDownloadOk() && chkCtl.getCheckOk()) {
+ //已经对账完成,切换日期
+ chkCtl = checkTransdtlService.doSwitchCheckCtlChkdate(chkCtl);
+ }
+
+ if (chkCtl.getDownloadOk()) {
+ //对账
+ doCheckProcess(chkCtl);
+ } else {
+ //下载对账单
+ chkCtl = downloadBillData(chkCtl);
+ if (chkCtl.getDownloadOk()) doCheckProcess(chkCtl);
+ }
+
+ long t2 = System.currentTimeMillis();
+ logger.info("=================== 对账任务结束,耗时 " + (t2 - t1) + " ms ===================");
+
+ }catch (Exception e){
+ logger.error("跟核心平台对账报错:" + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ private TCheckCtl downloadBillData(TCheckCtl ctl) {
+ String checkdate = ctl.getCheckdate();
+ String hostdate = systemUtilService.getSysdatetime().getHostdate();
+ if (DateUtil.compareDatetime(checkdate, hostdate, "yyyyMMdd") >= 0) {
+ logger.info("已对到当前日期!");
+ ctl.setRemark("已对到当前日期!");
+ return checkTransdtlService.doUpdateCheckCtl(ctl);
+ }
+
+ //下载对账单数据
+ TCheckFile checkFile = checkTransdtlService.doGetCheckFileRecordByAccdate(checkdate);
+ List<TShopSettlement> shoplist = checkTransdtlService.getAllSettlementShops();
+ String errmsg = null;
+ for (TShopSettlement shop : shoplist) {
+ try {
+ DownloadShopBillParam param = new DownloadShopBillParam();
+ param.setCheckdate(checkdate);
+ param.setShopaccno(shop.getShopid());
+ param.setDtltype("canteen");
+
+ String returnString = shopProxy.downloadShopBill(param);
+ if (StringUtil.isEmpty(returnString)) {
+ errmsg = "请求核心平台对账返回为空!";
+ break;
+ }
+
+ if (returnString.indexOf("retcode") != -1) {
+ //报错:
+ logger.error("shopaccno=[" + shop.getShopid() + "],checkdate=[" + checkdate + "],returnString=[" + returnString + "]");
+ try {
+ ApiResponse response = new Gson().fromJson(returnString, ApiResponse.class);
+ if (30005 == response.getRetcode()) {
+ continue; //无交易记录
+ } else {
+ errmsg = response.getRetmsg();
+ break;
+ }
+ } catch (Exception jxe) {
+ errmsg = "解析对账返回json数据报错!";
+ logger.error(errmsg);
+ break; //解析异常直接返回
+ }
+ } else {
+ //正确返回对账数据
+ try {
+ boolean ret = checkTransdtlService.saveCheckDetails(checkdate, shop.getShopid(), returnString);
+ if (!ret) {
+ errmsg = "shopaccno=[" + shop.getShopid() + "]保存交易明细数据失败!";
+ logger.error(errmsg);
+ break; //直接返回
+ }
+ } catch (Exception se) {
+ errmsg = "shopaccno=[" + shop.getShopid() + "]保存返回的交易明细报错!" + (se.getMessage() == null ? se.getClass().getName() : se.getMessage());
+ logger.error(errmsg);
+ se.printStackTrace();
+ break; //直接返回
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ errmsg = "shopaccno=["+shop.getShopid()+"]请求核心获取对账单抛出异常!" + (e.getMessage() == null ? e.getClass().getName() : e.getMessage());
+ break;
+ }
+ }
+
+ if (null == errmsg) {
+ //对账单下载完成
+ return checkTransdtlService.doFinishDownloadData(checkFile, ctl);
+ } else {
+ //对账单下载报错
+ ctl.setRemark(errmsg);
+ return checkTransdtlService.doUpdateCheckCtl(ctl);
+ }
+ }
+
+ private TCheckCtl doCheckProcess(TCheckCtl ctl){
+ if (!ctl.getDownloadOk()) {
+ logger.error("对账单还未下载完成!");
+ return ctl;
+ } else if (ctl.getCheckOk()) {
+ logger.info("checkdate=[" + ctl.getCheckdate() + "]已完成对账!");
+ return ctl;
+ }
+ TCheckFile checkFile = checkTransdtlService.getCheckFileByAccdate(ctl.getCheckdate());
+ if (null == checkFile) {
+ logger.error("对账单下载记录未找到!");
+ return ctl;
+ }
+
+ int pageSize = 1000;
+ for (int i = 0; i < checkFile.getOthercnt(); i += pageSize) {
+ try {
+ checkTransdtlService.doCheckEqualTransdtls(checkFile.getAccdate(), i, pageSize);
+ } catch (Exception e) {
+ logger.error("批量处理一致流水报错!");
+ }
+ }
+
+ List<TCheckDetail> detailList = checkTransdtlService.getUncheckDtlsByAccdate(checkFile.getAccdate());
+ for (TCheckDetail detail : detailList) {
+ try {
+ checkTransdtlService.doCheckSingleTransdtl(detail);
+ } catch (Exception e) {
+ }
+ }
+
+ //检查
+ if (checkTransdtlService.checkUncheckExists(checkFile.getAccdate())) {
+ logger.error("对账处理后还存在未对账的明细记录!");
+ ctl.setRemark("对账处理后还存在未对账的明细记录!");
+ return checkTransdtlService.doUpdateCheckCtl(ctl);
+ }
+
+ if (checkTransdtlService.doCheckLocalMoreDtls(checkFile.getAccdate()) > 0) {
+ //本地有多余成功流水
+ checkFile.setStatus(RestaurantConstant.CHKFILE_STATUS_FINISH);
+ checkFile.setChkresult(RestaurantConstant.CHKFILE_CHKRESULT_FAIL);
+ checkFile.setRemark("本地有多余成功流水在对账单中不存在!!!");
+ checkTransdtlService.doUpdateCheckFile(checkFile);
+
+ logger.error("checkdate=["+checkFile.getAccdate()+"]本地有多余成功流水在对账单中不存在!!!");
+ ctl.setRemark("本地有多余成功流水在对账单中不存在!");
+ return checkTransdtlService.doUpdateCheckCtl(ctl);
+ }
+
+ if (checkTransdtlService.checkFileHaveErrors(checkFile.getAccdate())) {
+ //对账存在 【金额不相等】【记账日期或结算商户错误】【本地交易流水不存在】 等挂起错误(不是补账能解决的错误)
+ checkFile.setStatus(RestaurantConstant.CHKFILE_STATUS_FINISH);
+ checkFile.setChkresult(RestaurantConstant.CHKFILE_CHKRESULT_FAIL);
+ checkFile.setRemark("核对完成,明细存在【挂起】异常!");
+ checkTransdtlService.doUpdateCheckFile(checkFile);
+
+ logger.error("checkdate=[" + checkFile.getAccdate() + "]核对完成,明细存在【挂起】异常!!!");
+ ctl.setRemark("核对完成,明细存在【挂起】异常!");
+ return checkTransdtlService.doUpdateCheckCtl(ctl);
+ }
+
+ //补账处理
+ List<TCheckDetail> needRepairList = checkTransdtlService.getNeedRepairChkdtls(checkFile.getAccdate());
+ for (TCheckDetail repDetail : needRepairList) {
+ try {
+ checkTransdtlService.doRepairCheckDetail(repDetail); //TODO:补账逻辑
+ } catch (Exception e) {
+ try {
+ repDetail.setResolved(RestaurantConstant.CHKDTL_RESOLVED_FAIL);
+ repDetail.setRemark(e.getMessage() != null ? e.getMessage() : e.getClass().getName());
+ checkTransdtlService.doUpdateCheckDetail(repDetail); //保存错误信息
+ } catch (Exception e1) {
+ }
+ }
+ }
+
+ return checkTransdtlService.doConfirmCheckFile(ctl, checkFile);
+ }
+
+}
diff --git a/src/main/java/com/supwisdom/dlpay/restaurant/util/RestaurantConstant.java b/src/main/java/com/supwisdom/dlpay/restaurant/util/RestaurantConstant.java
index 534fa61..e9afa21 100644
--- a/src/main/java/com/supwisdom/dlpay/restaurant/util/RestaurantConstant.java
+++ b/src/main/java/com/supwisdom/dlpay/restaurant/util/RestaurantConstant.java
@@ -48,4 +48,32 @@
public static final int POS_TIME_ERROR_DIFFMINS = 10; //设备时钟误差
public static final int TRANSDTL_STATUS_WIT=55555;
+ public static final String CHECK_TRANSDTL_CTLID = "1"; //对账表唯一记录主键
+
+ public static final String CHKFILE_STATUS_INIT = "init"; //初始化
+ public static final String CHKFILE_STATUS_UNCHECK = "uncheck"; //待对账
+ public static final String CHKFILE_STATUS_ERROR = "error"; //下载异常
+ public static final String CHKFILE_STATUS_FINISH = "finish"; //已处理对账
+
+ public static final String CHKFILE_CHKRESULT_NONE = "none"; //未对账
+ public static final String CHKFILE_CHKRESULT_SUCCESS = "success"; //对账成功
+ public static final String CHKFILE_CHKRESULT_FAIL = "fail"; //对账有错误
+
+ public static final String CHKDTL_CHKRESULT_UNCHECK = "uncheck"; //未对账
+ public static final String CHKDTL_CHKRESULT_EQUAL = "equal"; //一致
+ public static final String CHKDTL_CHKRESULT_NOTEXIST= "notexist"; //交易流水不存在
+ public static final String CHKDTL_CHKRESULT_NOCHARGE = "nocharge"; //支付未记账
+ public static final String CHKDTL_CHKRESULT_DIFF = "diff"; //金额不相等
+ public static final String CHKDTL_CHKRESULT_ERROR = "error"; //记账日期或支付方式错误
+ public static final String CHKDTL_CHKRESULT_SURPLUS = "surplus"; //本地多余成功流水
+
+
+ public static final String CHKDTL_RESOLVED_NONE = "none"; //未知结果
+ public static final String CHKDTL_RESOLVED_EQUAL = "equal"; //一致
+ public static final String CHKDTL_RESOLVED_ADD ="add"; //补账
+ public static final String CHKDTL_RESOLVED_FAIL = "fail"; //补账失败
+ public static final String CHKDTL_RESOLVED_HANGUP="hangup"; //挂起
+
+
+
}
diff --git a/src/main/kotlin/com/supwisdom/dlpay/RestaurantApplication.kt b/src/main/kotlin/com/supwisdom/dlpay/RestaurantApplication.kt
index fdbdb46..eb901a8 100644
--- a/src/main/kotlin/com/supwisdom/dlpay/RestaurantApplication.kt
+++ b/src/main/kotlin/com/supwisdom/dlpay/RestaurantApplication.kt
@@ -1,6 +1,8 @@
package com.supwisdom.dlpay
import io.lettuce.core.ReadFrom
+import net.javacrumbs.shedlock.core.LockProvider
+import net.javacrumbs.shedlock.provider.redis.spring.RedisLockProvider
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
@@ -43,6 +45,11 @@
serverConfig.database = redis.database
return LettuceConnectionFactory(serverConfig, clientConfig)
}
+
+ @Bean
+ fun lockProvider(connectionFactory: RedisConnectionFactory): LockProvider {
+ return RedisLockProvider(connectionFactory, "prod")
+ }
}
@Configuration
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 41eb523..f1cb684 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -31,3 +31,6 @@
cron.offlinedtl=0/10 * * * * ?
+# 对账任务
+restaurant.chkdtltask.cron=27 3/10 * * * ?
+
diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql
index 90a9ab5..e01f263 100644
--- a/src/main/resources/data.sql
+++ b/src/main/resources/data.sql
@@ -120,8 +120,9 @@
INSERT INTO "tb_function"("id", "createtime", "isleaf", "lastsaved", "menuicon", "menuurl", "name", "ordernum", "parentid") VALUES (13, NULL, 1, NULL, '', '/whitelist/index', '就餐白名单维护', 1, 11);
INSERT INTO "tb_function"("id", "createtime", "isleaf", "lastsaved", "menuicon", "menuurl", "name", "ordernum", "parentid") VALUES (18, NULL, 1, NULL, '', '/whitelistcheck/index', '就餐白名单审核', 2, 11);
INSERT INTO "tb_function"("id", "createtime", "isleaf", "lastsaved", "menuicon", "menuurl", "name", "ordernum", "parentid") VALUES (31, NULL, 1, NULL, '', '/whitelistbind/devbind', '就餐白名单设备绑定', 3, 11);
+
INSERT INTO "tb_function" (id,createtime,isleaf,lastsaved,menuicon,menuurl,name,ordernum,parentid) VALUES (11, NULL, 0, NULL, 'layui-icon-util', '#', '就餐白名单管理', 7, -1);
-INSERT INTO "tb_function"("id", "createtime", "isleaf", "lastsaved", "menuicon", "menuurl", "name", "ordernum", "parentid") VALUES (32, NULL, 1, NULL, '', '/shopsettlement/index', '商户管理', 2, 10);
+INSERT INTO "tb_function"("id", "createtime", "isleaf", "lastsaved", "menuicon", "menuurl", "name", "ordernum", "parentid") VALUES (32, NULL, 1, NULL, '', '/shopsettlement/index', '商户管理', 2, 22);
INSERT INTO "tb_function"("id", "createtime", "isleaf", "lastsaved", "menuicon", "menuurl", "name", "ordernum", "parentid") VALUES (34, NULL, 1, NULL, '', '/customerlist/index', '餐补人员名单管理', 1, 24);