对账
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);