diff --git a/src/main/java/com/ycwl/basic/biz/TaskStatusBiz.java b/src/main/java/com/ycwl/basic/biz/TaskStatusBiz.java
new file mode 100644
index 0000000..451d8ee
--- /dev/null
+++ b/src/main/java/com/ycwl/basic/biz/TaskStatusBiz.java
@@ -0,0 +1,192 @@
+package com.ycwl.basic.biz;
+
+import com.ycwl.basic.mapper.FaceMapper;
+import com.ycwl.basic.mapper.TaskMapper;
+import com.ycwl.basic.mapper.VideoMapper;
+import com.ycwl.basic.model.mobile.goods.VideoTaskStatusVO;
+import com.ycwl.basic.model.pc.face.entity.FaceEntity;
+import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
+import com.ycwl.basic.model.pc.task.req.TaskReqQuery;
+import com.ycwl.basic.model.pc.task.resp.TaskRespVO;
+import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
+import com.ycwl.basic.model.pc.video.entity.VideoEntity;
+import com.ycwl.basic.repository.FaceRepository;
+import com.ycwl.basic.repository.TemplateRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class TaskStatusBiz {
+    public static final String TASK_STATUS_USER_CACHE_KEY = "task:status:user:%s:face:%s";
+    public static final String TASK_STATUS_FACE_CACHE_KEY = "task:status:face:%s";
+    public static final String TASK_STATUS_FACE_CACHE_KEY_CUT = "task:status:face:%s:cut";
+    public static final String TASK_STATUS_FACE_CACHE_KEY_TEMPLATE = "task:status:face:%s:tpl:%s";
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+    @Autowired
+    private FaceRepository faceRepository;
+    @Autowired
+    private TemplateRepository templateRepository;
+    @Autowired
+    private FaceMapper faceMapper;
+    @Autowired
+    private TaskMapper taskMapper;
+    @Autowired
+    private VideoMapper videoMapper;
+    @Autowired
+    private TemplateBiz templateBiz;
+
+    public boolean getUserHaveFace(Long userId, Long faceId) {
+        if (userId == null || faceId == null) {
+            return false;
+        }
+        if (redisTemplate.hasKey(String.format(TASK_STATUS_USER_CACHE_KEY, userId, faceId))) {
+            return true;
+        }
+        FaceEntity face = faceRepository.getFace(faceId);
+        if (face == null) {
+            return false;
+        }
+        if (face.getMemberId().equals(userId)) {
+            redisTemplate.opsForValue().set(String.format(TASK_STATUS_USER_CACHE_KEY, userId, faceId), "1", 3600, TimeUnit.SECONDS);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public void setFaceCutStatus(Long faceId, int status) {
+        redisTemplate.opsForValue().set(String.format(TASK_STATUS_FACE_CACHE_KEY_CUT, faceId), String.valueOf(status), 3600, TimeUnit.SECONDS);
+    }
+
+    public void setFaceTemplateStatus(Long faceId, Long templateId, Long videoId) {
+        redisTemplate.opsForValue().set(String.format(TASK_STATUS_FACE_CACHE_KEY_TEMPLATE, faceId, templateId), String.valueOf(videoId), 3600, TimeUnit.SECONDS);
+    }
+
+    public VideoTaskStatusVO getScenicUserStatus(Long scenicId, Long userId) {
+        FaceRespVO lastFace = faceMapper.findLastFaceByScenicAndUserId(scenicId, userId);
+        VideoTaskStatusVO response = new VideoTaskStatusVO();
+        if (lastFace == null) {
+            response.setStatus(-1);
+            return response;
+        }
+        return getFaceStatus(lastFace.getId());
+    }
+
+    public VideoTaskStatusVO getFaceStatus(Long faceId) {
+        FaceEntity face = faceRepository.getFace(faceId);
+        VideoTaskStatusVO response = new VideoTaskStatusVO();
+        if (face == null) {
+            response.setStatus(-1);
+            return response;
+        }
+        response.setScenicId(face.getScenicId());
+        response.setFaceId(faceId);
+        List<TemplateRespVO> templateList = templateRepository.getTemplateListByScenicId(face.getScenicId());
+        response.setMaxCount(templateList.size());
+        int alreadyFinished = 0;
+        for (TemplateRespVO template : templateList) {
+            response.setTemplateId(template.getId());
+            long videoId = getFaceTemplateVideoId(faceId, template.getId());
+            if (videoId <= 0) {
+                response.setStatus(2);
+            } else {
+                response.setVideoId(videoId);
+                alreadyFinished++;
+            }
+        }
+        response.setCount(alreadyFinished);
+        if (alreadyFinished == 0) {
+            response.setStatus(0);
+        } else {
+            response.setStatus(1);
+        }
+        if (alreadyFinished == 0) {
+            int faceCutStatus = getFaceCutStatus(faceId);
+            if (faceCutStatus != 1) {
+                // 正在切片
+                if (templateBiz.determineTemplateCanGenerate(templateList.get(0).getId(), faceId, false)) {
+                    response.setStatus(2);
+                } else {
+                    response.setStatus(0);
+                }
+            }
+        }
+        return response;
+    }
+
+    public VideoTaskStatusVO getFaceTemplateStatus(Long faceId, Long templateId) {
+        FaceEntity face = faceRepository.getFace(faceId);
+        VideoTaskStatusVO response = new VideoTaskStatusVO();
+        if (face == null) {
+            response.setStatus(-1);
+            return response;
+        }
+        response.setScenicId(face.getScenicId());
+        response.setFaceId(faceId);
+        response.setTemplateId(templateId);
+        long videoId = getFaceTemplateVideoId(faceId, templateId);
+        if (videoId < 0) {
+            int faceCutStatus = getFaceCutStatus(faceId);
+            if (faceCutStatus != 1) {
+                // 正在切片
+                response.setStatus(2);
+                return response;
+            }
+        } else if (videoId == 0) {
+            response.setStatus(2);
+        } else {
+            response.setVideoId(videoId);
+            response.setStatus(1);
+        }
+        return response;
+    }
+
+    public int getFaceCutStatus(Long faceId) {
+        if (redisTemplate.hasKey(String.format(TASK_STATUS_FACE_CACHE_KEY_CUT, faceId))) {
+            String status = redisTemplate.opsForValue().get(String.format(TASK_STATUS_FACE_CACHE_KEY_CUT, faceId));
+            if (status != null) {
+                return Integer.parseInt(status);
+            }
+        }
+        return 1;
+    }
+
+    public long getFaceTemplateVideoId(Long faceId, Long templateId) {
+        if (redisTemplate.hasKey(String.format(TASK_STATUS_FACE_CACHE_KEY_TEMPLATE, faceId, templateId))) {
+            String status = redisTemplate.opsForValue().get(String.format(TASK_STATUS_FACE_CACHE_KEY_TEMPLATE, faceId, templateId));
+            if (status != null) {
+                return Long.parseLong(status);
+            }
+        }
+        TaskReqQuery taskReqQuery = new TaskReqQuery();
+        taskReqQuery.setFaceId(faceId);
+        taskReqQuery.setTemplateId(templateId);
+        List<TaskRespVO> list = taskMapper.list(taskReqQuery);
+        Optional<TaskRespVO> min = list.stream().min(Comparator.comparing(TaskRespVO::getCreateTime));
+        if (min.isPresent()) {
+            TaskRespVO task = min.get();
+            long taskStatus = 0;
+            if (task.getStatus() == 1) {
+                // 已完成
+                VideoEntity video = videoMapper.findByTaskId(task.getId());
+                if (video != null) {
+                    taskStatus = video.getId();
+                }
+            }
+            setFaceTemplateStatus(faceId, templateId, taskStatus);
+        } else {
+            // 从来没生成过
+            setFaceTemplateStatus(faceId, templateId, -1L);
+            return -1;
+        }
+        return 0;
+    }
+}
diff --git a/src/main/java/com/ycwl/basic/controller/mobile/AppGoodsController.java b/src/main/java/com/ycwl/basic/controller/mobile/AppGoodsController.java
index a1caf6a..160c07d 100644
--- a/src/main/java/com/ycwl/basic/controller/mobile/AppGoodsController.java
+++ b/src/main/java/com/ycwl/basic/controller/mobile/AppGoodsController.java
@@ -1,6 +1,7 @@
 package com.ycwl.basic.controller.mobile;
 
 import com.ycwl.basic.annotation.IgnoreToken;
+import com.ycwl.basic.biz.TaskStatusBiz;
 import com.ycwl.basic.constant.BaseContextHandler;
 import com.ycwl.basic.exception.CheckTokenException;
 import com.ycwl.basic.model.jwt.JwtInfo;
@@ -31,6 +32,8 @@ public class AppGoodsController {
     private GoodsService goodsService;
     @Autowired
     private TaskService taskService;
+    @Autowired
+    private TaskStatusBiz taskStatusBiz;
 
     @ApiOperation("商品列表")
     @PostMapping("/goodsList")
diff --git a/src/main/java/com/ycwl/basic/controller/mobile/AppTaskController.java b/src/main/java/com/ycwl/basic/controller/mobile/AppTaskController.java
index 0e63d13..90be752 100644
--- a/src/main/java/com/ycwl/basic/controller/mobile/AppTaskController.java
+++ b/src/main/java/com/ycwl/basic/controller/mobile/AppTaskController.java
@@ -1,6 +1,7 @@
 package com.ycwl.basic.controller.mobile;
 
 import com.ycwl.basic.annotation.IgnoreLogReq;
+import com.ycwl.basic.biz.TaskStatusBiz;
 import com.ycwl.basic.model.jwt.JwtInfo;
 import com.ycwl.basic.model.mobile.goods.VideoTaskReq;
 import com.ycwl.basic.model.mobile.goods.VideoTaskStatusVO;
@@ -24,6 +25,8 @@ public class AppTaskController {
     private GoodsService goodsService;
     @Autowired
     private TaskService taskService;
+    @Autowired
+    private TaskStatusBiz taskStatusBiz;
 
     @GetMapping("/face/{faceId}")
     @IgnoreLogReq
diff --git a/src/main/java/com/ycwl/basic/controller/viid/ViidController.java b/src/main/java/com/ycwl/basic/controller/viid/ViidController.java
index 06424d8..74c65dc 100644
--- a/src/main/java/com/ycwl/basic/controller/viid/ViidController.java
+++ b/src/main/java/com/ycwl/basic/controller/viid/ViidController.java
@@ -273,7 +273,9 @@ public class ViidController {
                             faceSampleMapper.add(faceSample);
                             new Thread(() -> {
                                 taskFaceService.addFaceSample(faceSample.getId());
-                                DynamicTaskGenerator.addTask(faceSample.getId());
+                                if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
+                                    DynamicTaskGenerator.addTask(faceSample.getId());
+                                }
                             }).start();
                             for (SubImageInfoObject _subImage : type14ImageList) {
                                 facePosition.setImgHeight(_subImage.getHeight());
@@ -318,7 +320,12 @@ public class ViidController {
                             faceSample.setFaceUrl(url);
                             faceSampleMapper.add(faceSample);
                             DynamicTaskGenerator.addTask(faceSample.getId());
-                            taskFaceService.addFaceSample(faceSample.getId());
+                            new Thread(() -> {
+                                taskFaceService.addFaceSample(faceSample.getId());
+                                if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) {
+                                    DynamicTaskGenerator.addTask(faceSample.getId());
+                                }
+                            }).start();
                             log.info("模式1人脸信息入库成功!设备ID:{}", deviceID);
                         }
                     }
diff --git a/src/main/java/com/ycwl/basic/enums/StatisticEnum.java b/src/main/java/com/ycwl/basic/enums/StatisticEnum.java
index a41b744..49ce5b5 100644
--- a/src/main/java/com/ycwl/basic/enums/StatisticEnum.java
+++ b/src/main/java/com/ycwl/basic/enums/StatisticEnum.java
@@ -12,8 +12,9 @@ public enum StatisticEnum {
     REFUND(5,"退款"),
     MESSAGE_PUSH(6,"消息推送"),
     DOWNLOAD(8,"下载"),
-    CLICK_ON_PAYMENT(9,"点击支付、购买"),
+    CLICK_PAY(9,"点击支付"),
     OTHER_ENTER(10,"其他渠道进入"),
+    SUBMIT_PAYMENT(11,"点击支付"),
     SCAN_MARKED_CODE(20,"扫描特殊标记码"),
 
     ;
diff --git a/src/main/java/com/ycwl/basic/interceptor/AuthInterceptor.java b/src/main/java/com/ycwl/basic/interceptor/AuthInterceptor.java
index 16d82e1..9ac3312 100644
--- a/src/main/java/com/ycwl/basic/interceptor/AuthInterceptor.java
+++ b/src/main/java/com/ycwl/basic/interceptor/AuthInterceptor.java
@@ -66,7 +66,7 @@ public class AuthInterceptor extends HandlerInterceptorAdapter {
         // 获取 token
         String token = getToken(request);
         if (StringUtils.isEmpty(token)) {
-            log.error("==>  请求 header 缺少 Token [{}]", token);
+            log.error("==>  请求 header 缺少 Token [{}], URL [{}]", token, request.getRequestURL());
             throw new MissTokenException("请求头缺少token");
         }
 
diff --git a/src/main/java/com/ycwl/basic/mapper/FaceMapper.java b/src/main/java/com/ycwl/basic/mapper/FaceMapper.java
index f61dbd3..7fd1c2d 100644
--- a/src/main/java/com/ycwl/basic/mapper/FaceMapper.java
+++ b/src/main/java/com/ycwl/basic/mapper/FaceMapper.java
@@ -24,11 +24,12 @@ public interface FaceMapper {
     int deleteByIds(@Param("list") List<Long> ids);
     int update(FaceEntity face);
 
-    FaceRespVO getByMemberId(@Param("userId") Long userId, @Param("scenicId") Long scenicId);
+    FaceRespVO getLatestByMemberId(@Param("userId") Long userId, @Param("scenicId") Long scenicId);
 
     int finishedJourney(Long faceId);
 
     FaceRespVO findLastFaceByUserId(String userId);
+    FaceRespVO findLastFaceByScenicAndUserId(Long scenicId, Long userId);
 
     List<FaceRespVO> listByScenicAndUserId(String scenicId, Long userId);
 }
diff --git a/src/main/java/com/ycwl/basic/mapper/FaceSampleMapper.java b/src/main/java/com/ycwl/basic/mapper/FaceSampleMapper.java
index 35e8d81..bf7bc17 100644
--- a/src/main/java/com/ycwl/basic/mapper/FaceSampleMapper.java
+++ b/src/main/java/com/ycwl/basic/mapper/FaceSampleMapper.java
@@ -26,5 +26,5 @@ public interface FaceSampleMapper {
     List<FaceSampleEntity> listByIds(List<Long> list);
 
     FaceSampleEntity getEntity(Long faceSampleId);
-    List<FaceSampleEntity> listEntity(Long scenicId, Date endDate);
+    List<FaceSampleEntity> listEntityBeforeDate(Long scenicId, Date endDate);
 }
diff --git a/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceConfigEntity.java b/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceConfigEntity.java
index 1c0eed6..3748344 100644
--- a/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceConfigEntity.java
+++ b/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceConfigEntity.java
@@ -57,4 +57,5 @@ public class DeviceConfigEntity {
      * 切割时,取人脸后多少秒的视频
      */
     private BigDecimal cutPost;
+    private Integer enablePreBook;
 }
diff --git a/src/main/java/com/ycwl/basic/model/pc/faceDetectLog/entity/FaceDetectLog.java b/src/main/java/com/ycwl/basic/model/pc/faceDetectLog/entity/FaceDetectLog.java
index 4aada80..5e9b4dc 100644
--- a/src/main/java/com/ycwl/basic/model/pc/faceDetectLog/entity/FaceDetectLog.java
+++ b/src/main/java/com/ycwl/basic/model/pc/faceDetectLog/entity/FaceDetectLog.java
@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest;
 import com.aliyuncs.facebody.model.v20191230.SearchFaceResponse;
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.ycwl.basic.model.pc.faceDetectLog.resp.MatchLocalRecord;
@@ -31,6 +32,9 @@ public class FaceDetectLog {
 
     private String matchLocalRecord;
 
+    @TableField(exist = false)
+    private List<SearchFaceResponse.Data.MatchListItem.FaceItemsItem> matchRawRecord;
+
     public static FaceDetectLog quickCreate(String reason) {
         FaceDetectLog log = new FaceDetectLog();
         log.reason = reason;
diff --git a/src/main/java/com/ycwl/basic/model/pc/faceDetectLog/resp/MatchLocalRecord.java b/src/main/java/com/ycwl/basic/model/pc/faceDetectLog/resp/MatchLocalRecord.java
index c50e368..afd520f 100644
--- a/src/main/java/com/ycwl/basic/model/pc/faceDetectLog/resp/MatchLocalRecord.java
+++ b/src/main/java/com/ycwl/basic/model/pc/faceDetectLog/resp/MatchLocalRecord.java
@@ -7,6 +7,7 @@ import java.util.Date;
 @Data
 public class MatchLocalRecord {
     private Long faceSampleId;
+    private String deviceName;
     private String faceUrl;
     private Float score;
     private Float confidence;
diff --git a/src/main/java/com/ycwl/basic/model/pc/scenic/entity/ScenicConfigEntity.java b/src/main/java/com/ycwl/basic/model/pc/scenic/entity/ScenicConfigEntity.java
index 316e810..372e6d4 100644
--- a/src/main/java/com/ycwl/basic/model/pc/scenic/entity/ScenicConfigEntity.java
+++ b/src/main/java/com/ycwl/basic/model/pc/scenic/entity/ScenicConfigEntity.java
@@ -43,6 +43,7 @@ public class ScenicConfigEntity {
      * 预约流程,1-预约,2-在线,3-全部
      */
     private Integer bookRoutine;
+    private Integer forceFinishTime;
     /**
      * 样本保存时间
      */
diff --git a/src/main/java/com/ycwl/basic/ratelimiter/FixedRateLimiter.java b/src/main/java/com/ycwl/basic/ratelimiter/FixedRateLimiter.java
index 70546c4..f975c75 100644
--- a/src/main/java/com/ycwl/basic/ratelimiter/FixedRateLimiter.java
+++ b/src/main/java/com/ycwl/basic/ratelimiter/FixedRateLimiter.java
@@ -5,28 +5,36 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
-public class FixedRateLimiter {
+public class FixedRateLimiter implements IRateLimiter {
     private final Semaphore semaphore = new Semaphore(1);
     private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
 
     public FixedRateLimiter(int rate, TimeUnit timeUnit) {
         // 启动一个线程每0.5秒释放一个许可
         scheduler.scheduleAtFixedRate(() -> {
-            synchronized (semaphore) {
-                if (semaphore.availablePermits() < 1) {
-                    semaphore.release(1);
-                }
+            if (semaphore.availablePermits() < 1) {
+                semaphore.release(1);
             }
         }, rate, rate, timeUnit);
     }
 
+    @Override
     public void acquire() throws InterruptedException {
-        synchronized (semaphore) {
-            semaphore.acquire();
-        }
+        semaphore.acquire();
     }
 
+    @Override
     public void shutdown() {
         scheduler.shutdown();
     }
+
+    @Override
+    public boolean tryAcquire() {
+        return semaphore.tryAcquire();
+    }
+
+    @Override
+    public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
+        return semaphore.tryAcquire(timeout, unit);
+    }
 }
diff --git a/src/main/java/com/ycwl/basic/ratelimiter/IRateLimiter.java b/src/main/java/com/ycwl/basic/ratelimiter/IRateLimiter.java
new file mode 100644
index 0000000..bdcbbe3
--- /dev/null
+++ b/src/main/java/com/ycwl/basic/ratelimiter/IRateLimiter.java
@@ -0,0 +1,10 @@
+package com.ycwl.basic.ratelimiter;
+
+import java.util.concurrent.TimeUnit;
+
+public interface IRateLimiter {
+    void acquire() throws InterruptedException;
+    void shutdown();
+    boolean tryAcquire();
+    boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException;
+}
diff --git a/src/main/java/com/ycwl/basic/ratelimiter/SlidingWindowRateLimiter.java b/src/main/java/com/ycwl/basic/ratelimiter/SlidingWindowRateLimiter.java
index e815ee3..99571f5 100644
--- a/src/main/java/com/ycwl/basic/ratelimiter/SlidingWindowRateLimiter.java
+++ b/src/main/java/com/ycwl/basic/ratelimiter/SlidingWindowRateLimiter.java
@@ -6,25 +6,45 @@ import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 
-public class SlidingWindowRateLimiter {
+public class SlidingWindowRateLimiter implements IRateLimiter {
     private final Semaphore semaphore;
     private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
 
     public SlidingWindowRateLimiter(int maxRequestsPerSecond) {
         this.semaphore = new Semaphore(maxRequestsPerSecond);
-        int rate = 1000000 / maxRequestsPerSecond;
         scheduler.scheduleAtFixedRate(() -> {
             if (semaphore.availablePermits() < maxRequestsPerSecond) {
-                semaphore.release(1);
+                semaphore.release(maxRequestsPerSecond - semaphore.availablePermits());
             }
-        }, rate, rate, TimeUnit.MICROSECONDS);
+        }, 1, 1, TimeUnit.SECONDS);
     }
 
+    public SlidingWindowRateLimiter(int maxRequests, int perSecond) {
+        this.semaphore = new Semaphore(maxRequests);
+        scheduler.scheduleAtFixedRate(() -> {
+            if (semaphore.availablePermits() < maxRequests) {
+                semaphore.release(maxRequests - semaphore.availablePermits());
+            }
+        }, perSecond, perSecond, TimeUnit.SECONDS);
+    }
+
+    @Override
     public void acquire() throws InterruptedException {
         semaphore.acquire();
     }
 
+    @Override
     public void shutdown() {
         scheduler.shutdown();
     }
+
+    @Override
+    public boolean tryAcquire() {
+        return semaphore.tryAcquire();
+    }
+
+    @Override
+    public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
+        return semaphore.tryAcquire(timeout, unit);
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/com/ycwl/basic/service/impl/mobile/GoodsServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/mobile/GoodsServiceImpl.java
index 8864165..c6ec3c5 100644
--- a/src/main/java/com/ycwl/basic/service/impl/mobile/GoodsServiceImpl.java
+++ b/src/main/java/com/ycwl/basic/service/impl/mobile/GoodsServiceImpl.java
@@ -346,7 +346,7 @@ public class GoodsServiceImpl implements GoodsService {
 
     @Override
     public VideoTaskStatusVO getTaskStatusByScenicId(Long userId, Long scenicId) {
-        FaceRespVO faceVO = faceMapper.getByMemberId(userId, scenicId);
+        FaceRespVO faceVO = faceMapper.getLatestByMemberId(userId, scenicId);
         VideoTaskStatusVO response = new VideoTaskStatusVO();
         response.setScenicId(scenicId);
         if (faceVO == null) {
diff --git a/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java
index 3ad639d..fb69173 100644
--- a/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java
+++ b/src/main/java/com/ycwl/basic/service/impl/pc/OrderServiceImpl.java
@@ -326,14 +326,6 @@ public class OrderServiceImpl implements OrderService {
             return ApiResponse.fail("订单添加失败");
         }
         //点击支付按钮统计
-        OrderRespVO orderRespVO = orderMapper.getById(orderId);
-        StatisticsRecordAddReq statisticsRecordAddReq = new StatisticsRecordAddReq();
-        statisticsRecordAddReq.setMemberId(orderRespVO.getMemberId());
-        statisticsRecordAddReq.setType(StatisticEnum.CLICK_ON_PAYMENT.code);
-        statisticsRecordAddReq.setScenicId(orderRespVO.getScenicId());
-        statisticsRecordAddReq.setMorphId(orderId);
-        statisticsMapper.addStatisticsRecord(statisticsRecordAddReq);
-
 
         WxPayRespVO wxPayRespVO = initiatePayment(order, orderItems);
         return ApiResponse.success(wxPayRespVO);
diff --git a/src/main/java/com/ycwl/basic/service/task/impl/TaskFaceServiceImpl.java b/src/main/java/com/ycwl/basic/service/task/impl/TaskFaceServiceImpl.java
index 0c180cf..2c76675 100644
--- a/src/main/java/com/ycwl/basic/service/task/impl/TaskFaceServiceImpl.java
+++ b/src/main/java/com/ycwl/basic/service/task/impl/TaskFaceServiceImpl.java
@@ -1,9 +1,7 @@
 package com.ycwl.basic.service.task.impl;
 
-import cn.hutool.core.date.DateUtil;
 import com.alibaba.fastjson.JSON;
 import com.aliyuncs.exceptions.ClientException;
-import com.aliyuncs.exceptions.ServerException;
 import com.aliyuncs.facebody.model.v20191230.AddFaceEntityRequest;
 import com.aliyuncs.facebody.model.v20191230.AddFaceRequest;
 import com.aliyuncs.facebody.model.v20191230.AddFaceResponse;
@@ -16,7 +14,6 @@ import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesRequest;
 import com.aliyuncs.facebody.model.v20191230.ListFaceEntitiesResponse;
 import com.aliyuncs.facebody.model.v20191230.SearchFaceRequest;
 import com.aliyuncs.facebody.model.v20191230.SearchFaceResponse;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ycwl.basic.biz.OrderBiz;
 import com.ycwl.basic.config.FaceDetectConfig;
 import com.ycwl.basic.constant.FaceConstant;
@@ -27,22 +24,20 @@ import com.ycwl.basic.mapper.FaceSampleMapper;
 import com.ycwl.basic.mapper.ScenicMapper;
 import com.ycwl.basic.mapper.SourceMapper;
 import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
+import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
 import com.ycwl.basic.model.pc.face.entity.FaceEntity;
 import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
 import com.ycwl.basic.model.pc.faceDetectLog.entity.FaceDetectLog;
 import com.ycwl.basic.model.pc.faceDetectLog.resp.MatchLocalRecord;
 import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
-import com.ycwl.basic.model.pc.faceSample.req.FaceSampleReqQuery;
 import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
 import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
-import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
-import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
 import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
 import com.ycwl.basic.model.pc.source.entity.SourceEntity;
 import com.ycwl.basic.model.task.resp.AddFaceSampleRespVo;
 import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
 import com.ycwl.basic.ratelimiter.FixedRateLimiter;
-import com.ycwl.basic.ratelimiter.SlidingWindowRateLimiter;
+import com.ycwl.basic.repository.DeviceRepository;
 import com.ycwl.basic.repository.FaceRepository;
 import com.ycwl.basic.repository.ScenicRepository;
 import com.ycwl.basic.service.task.TaskFaceService;
@@ -55,7 +50,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import com.aliyuncs.DefaultAcsClient;
 import com.aliyuncs.IAcsClient;
@@ -103,6 +97,8 @@ public class TaskFaceServiceImpl implements TaskFaceService {
 
     @Autowired
     private ScenicRepository scenicRepository;
+    @Autowired
+    private DeviceRepository deviceRepository;
 
     private IAcsClient getClient() {
         DefaultProfile profile = DefaultProfile.getProfile(
@@ -195,29 +191,13 @@ public class TaskFaceServiceImpl implements TaskFaceService {
             if (matchList.get(0).getFaceItems().isEmpty()) {
                 return respVo;
             }
-            List<MatchLocalRecord> records = matchList.get(0).getFaceItems().stream()
-                    .map(item -> {
-                        MatchLocalRecord record = new MatchLocalRecord();
-                        record.setIdStr(item.getExtraData());
-                        record.setFaceSampleId(Long.parseLong(item.getExtraData()));
-                        if (StringUtils.isNumeric(item.getDbName())) {
-                            FaceSampleEntity faceSample = faceRepository.getFaceSample(record.getFaceSampleId());
-                            if (faceSample != null) {
-                                record.setFaceUrl(faceSample.getFaceUrl());
-                                record.setShotDate(faceSample.getCreateAt());
-                            }
-                        } else {
-                            record.setFaceUrl(getFaceUrl(record.getFaceSampleId()));
-                        }
-                        record.setScore(item.getScore());
-                        record.setConfidence(item.getConfidence());
-                        return record;
-                    })
-                    .collect(Collectors.toList());
-            log.matchLocalRecord(records);
+            List<SearchFaceResponse.Data.MatchListItem.FaceItemsItem> records = matchList.get(0).getFaceItems();
+            log.setMatchRawRecord(records);
             List<Long> faceSampleIds = records.stream()
                     .filter(record -> record.getScore() > 0.525F)
-                    .map(MatchLocalRecord::getFaceSampleId)
+                    .map(SearchFaceResponse.Data.MatchListItem.FaceItemsItem::getExtraData)
+                    .filter(StringUtils::isNumeric)
+                    .map(Long::valueOf)
                     .collect(Collectors.toList());
             respVo.setFirstMatchRate(matchList.get(0).getFaceItems().get(0).getScore());
             respVo.setSampleListIds(faceSampleIds);
@@ -227,7 +207,33 @@ public class TaskFaceServiceImpl implements TaskFaceService {
             e.printStackTrace();
             throw new BaseException(e.getMessage());
         } finally {
-            logMapper.insert(log);
+            new Thread(() -> {
+                if (log.getMatchRawRecord() != null) {
+                    List<MatchLocalRecord> collect = log.getMatchRawRecord().parallelStream().map(item -> {
+                        MatchLocalRecord record = new MatchLocalRecord();
+                        record.setIdStr(item.getExtraData());
+                        record.setFaceSampleId(Long.parseLong(item.getExtraData()));
+                        if (StringUtils.isNumeric(item.getDbName())) {
+                            FaceSampleEntity faceSample = faceRepository.getFaceSample(record.getFaceSampleId());
+                            if (faceSample != null) {
+                                DeviceEntity device = deviceRepository.getDevice(faceSample.getDeviceId());
+                                if (device != null) {
+                                    record.setDeviceName(device.getName());
+                                }
+                                record.setFaceUrl(faceSample.getFaceUrl());
+                                record.setShotDate(faceSample.getCreateAt());
+                            }
+                        } else {
+                            record.setFaceUrl(getFaceUrl(record.getFaceSampleId()));
+                        }
+                        record.setScore(item.getScore());
+                        record.setConfidence(item.getConfidence());
+                        return record;
+                    }).collect(Collectors.toList());
+                    log.setMatchLocalRecord(JSON.toJSONString(collect));
+                }
+                logMapper.insert(log);
+            }).start();
         }
     }
 
@@ -289,20 +295,6 @@ public class TaskFaceServiceImpl implements TaskFaceService {
             sampleStoreDay = 7;
         }
         Date endDate = DateUtils.addDateDays(new Date(), -(sampleStoreDay + 1));
-        List<FaceSampleEntity> faceSampleList = faceSampleMapper.listEntity(scenicId, endDate);
-        if (faceSampleList.isEmpty()) {
-            log.info("当前景区{},人脸样本为空", scenicId);
-            return;
-        }
-        faceSampleList.forEach(faceSample -> {
-            boolean success = deleteFaceSample(String.valueOf(scenicId), generateEntityId(faceSample));
-            if (success) {
-                log.info("当前景区{},人脸样本ID{},删除成功", scenicId, faceSample.getId());
-                faceSampleMapper.deleteById(faceSample.getId());
-            } else {
-                log.info("当前景区{},人脸样本ID{},删除失败", scenicId, faceSample.getId());
-            }
-        });
         ListFaceEntitiesRequest listFaceEntitiesRequest = new ListFaceEntitiesRequest();
         listFaceEntitiesRequest.setDbName(String.valueOf(scenicId));
         listFaceEntitiesRequest.setOrder("asc");
@@ -342,6 +334,20 @@ public class TaskFaceServiceImpl implements TaskFaceService {
             }
         } catch (Exception ignored) {
         }
+        List<FaceSampleEntity> faceSampleList = faceSampleMapper.listEntityBeforeDate(scenicId, endDate);
+        if (faceSampleList.isEmpty()) {
+            log.info("当前景区{},人脸样本为空", scenicId);
+            return;
+        }
+        faceSampleList.forEach(faceSample -> {
+            boolean success = deleteFaceSample(String.valueOf(scenicId), generateEntityId(faceSample));
+            if (success) {
+                log.info("当前景区{},人脸样本ID{},删除成功", scenicId, faceSample.getId());
+                faceSampleMapper.deleteById(faceSample.getId());
+            } else {
+                log.info("当前景区{},人脸样本ID{},删除失败", scenicId, faceSample.getId());
+            }
+        });
     }
 
     @Override
diff --git a/src/main/java/com/ycwl/basic/service/task/impl/TaskTaskServiceImpl.java b/src/main/java/com/ycwl/basic/service/task/impl/TaskTaskServiceImpl.java
index 4f4a4a1..5c12801 100644
--- a/src/main/java/com/ycwl/basic/service/task/impl/TaskTaskServiceImpl.java
+++ b/src/main/java/com/ycwl/basic/service/task/impl/TaskTaskServiceImpl.java
@@ -345,7 +345,8 @@ public class TaskTaskServiceImpl implements TaskService {
         if (templateList == null || templateList.isEmpty()) {
             return;
         }
-        if (Integer.valueOf(3).equals(scenicConfig.getBookRoutine())) {
+        if (Integer.valueOf(3).equals(scenicConfig.getBookRoutine()) || Integer.valueOf(4).equals(scenicConfig.getBookRoutine())) {
+            // 生成全部视频的逻辑
             templateList.forEach(template -> {
                 createTaskByFaceIdAndTempalteId(faceId, template.getId(), 1);
             });
diff --git a/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java b/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java
index 8c97dda..424e84c 100644
--- a/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java
+++ b/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java
@@ -90,7 +90,7 @@ public class DynamicTaskGenerator {
         queue.add(new Task(faceSampleId, createTime));
     }
 
-    @Scheduled(fixedDelay = 500L)
+    @Scheduled(fixedDelay = 1000L)
     public void doTask() {
         Task task = queue.poll();
         if (task == null) {
@@ -111,7 +111,7 @@ public class DynamicTaskGenerator {
             log.debug("当前景区{},无配置", faceSample.getScenicId());
             return;
         }
-        if (!Integer.valueOf(1).equals(scenicConfig.getBookRoutine()) && !Integer.valueOf(3).equals(scenicConfig.getBookRoutine())) {
+        if (!Integer.valueOf(5).equals(scenicConfig.getBookRoutine())) {
             log.debug("当前景区{}未启用预约流程,跳过", faceSample.getScenicId());
             return;
         }
diff --git a/src/main/java/com/ycwl/basic/task/FaceCleaner.java b/src/main/java/com/ycwl/basic/task/FaceCleaner.java
index 30102f7..1bd68b9 100644
--- a/src/main/java/com/ycwl/basic/task/FaceCleaner.java
+++ b/src/main/java/com/ycwl/basic/task/FaceCleaner.java
@@ -3,10 +3,15 @@ package com.ycwl.basic.task;
 import com.ycwl.basic.mapper.FaceSampleMapper;
 import com.ycwl.basic.mapper.ScenicMapper;
 import com.ycwl.basic.mapper.SourceMapper;
+import com.ycwl.basic.mapper.VideoMapper;
 import com.ycwl.basic.model.pc.faceSample.req.FaceSampleReqQuery;
 import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
+import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
 import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
 import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
+import com.ycwl.basic.model.pc.video.req.VideoReqQuery;
+import com.ycwl.basic.model.pc.video.resp.VideoRespVO;
+import com.ycwl.basic.repository.ScenicRepository;
 import com.ycwl.basic.service.task.TaskFaceService;
 import com.ycwl.basic.storage.StorageFactory;
 import com.ycwl.basic.storage.adapters.IStorageAdapter;
@@ -31,6 +36,10 @@ public class FaceCleaner {
     private FaceSampleMapper faceSampleMapper;
     @Autowired
     private SourceMapper sourceMapper;
+    @Autowired
+    private VideoMapper videoMapper;
+    @Autowired
+    private ScenicRepository scenicRepository;
 
     @Scheduled(cron = "0 0 4 * * ?")
     public void clean(){
@@ -44,11 +53,43 @@ public class FaceCleaner {
 
     @Scheduled(cron = "0 0 3 * * ?")
     public void deleteExpiredSource(){
-
+        ScenicReqQuery scenicQuery = new ScenicReqQuery();
+        List<ScenicRespVO> scenicList = scenicMapper.list(scenicQuery);
+        scenicList.parallelStream().forEach(scenic -> {
+            ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenic.getId());
+            if (scenicConfig == null) {
+                log.info("当前景区{},无配置信息", scenic.getName());
+                return;
+            }
+            int imageSourceExpireDay = 7;
+            int videoSourceExpireDay = 7;
+            if (scenicConfig.getImageSourceStoreDay() != null) {
+                imageSourceExpireDay = scenicConfig.getImageSourceStoreDay();
+            } else {
+                log.info("当前景区{},原始素材保存天数未设置,默认7天", scenic.getName());
+            }
+            if (scenicConfig.getVideoSourceStoreDay() != null) {
+                videoSourceExpireDay = scenicConfig.getVideoSourceStoreDay();
+            } else {
+                log.info("当前景区{},原始素材保存天数未设置,默认7天", scenic.getName());
+            }
+            if (Integer.valueOf(1).equals(scenicConfig.getDisableSourceVideo())) {
+                return;
+            }
+            if (Integer.valueOf(1).equals(scenicConfig.getDisableSourceImage())) {
+                return;
+            }
+            log.info("当前景区{},开始删除原始素材", scenic.getName());
+        });
     }
 
     @Scheduled(cron = "0 0 5 * * ?")
-    public void clear(){
+    public void clearOss(){
+        cleanFaceSampleOss();
+        cleanSourceOss();
+        cleanVideoOss();
+    }
+    private void cleanFaceSampleOss() {
         log.info("开始清理人脸文件");
         List<FaceSampleRespVO> faceSampleRespVOS = faceSampleMapper.list(new FaceSampleReqQuery());
         IStorageAdapter adapter = StorageFactory.use("faces");
@@ -60,4 +101,20 @@ public class FaceCleaner {
             }
         });
     }
+    private void cleanSourceOss() {
+    }
+    private void cleanVideoOss() {
+        log.info("开始清理视频文件");
+        List<VideoRespVO> videoRespVOS = videoMapper.list(new VideoReqQuery());
+        IStorageAdapter adapter = StorageFactory.use("video");
+        List<StorageFileObject> fileObjectList = adapter.listDir("");
+        fileObjectList.parallelStream().forEach(fileObject -> {
+            if (videoRespVOS.parallelStream().noneMatch(videoRespVO -> videoRespVO.getVideoUrl().contains(fileObject.getFullPath()))){
+                log.info("删除视频文件:{}", fileObject);
+                adapter.deleteFile(fileObject.getFullPath());
+            } else {
+                log.info("视频文件存在关系:{},未删除", fileObject);
+            }
+        });
+    }
 }
diff --git a/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java b/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java
index ed41f67..a2db559 100644
--- a/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java
+++ b/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java
@@ -1,18 +1,17 @@
 package com.ycwl.basic.task;
 
 import com.ycwl.basic.biz.OrderBiz;
+import com.ycwl.basic.biz.TaskStatusBiz;
 import com.ycwl.basic.device.DeviceFactory;
 import com.ycwl.basic.device.entity.common.FileObject;
 import com.ycwl.basic.device.operator.IDeviceStorageOperator;
 import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
 import com.ycwl.basic.repository.DeviceRepository;
-import com.ycwl.basic.mapper.DeviceMapper;
 import com.ycwl.basic.mapper.FaceSampleMapper;
 import com.ycwl.basic.mapper.SourceMapper;
 import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
 import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
 import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
-import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
 import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
 import com.ycwl.basic.model.pc.source.entity.SourceEntity;
 import com.ycwl.basic.repository.TemplateRepository;
@@ -34,7 +33,6 @@ import java.io.InputStreamReader;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -57,6 +55,8 @@ public class VideoPieceGetter {
     private OrderBiz orderBiz;
     @Autowired
     private TemplateRepository templateRepository;
+    @Autowired
+    private TaskStatusBiz taskStatusBiz;
 
     @Data
     public static class Task {
@@ -97,6 +97,9 @@ public class VideoPieceGetter {
         } else {
             templatePlaceholder = null;
         }
+        if (task.faceId != null) {
+            taskStatusBiz.setFaceCutStatus(task.faceId, 0);
+        }
         AtomicBoolean invoke = new AtomicBoolean(false);
         List<String> currentPlaceholder = new ArrayList<>();
         List<FaceSampleEntity> list = faceSampleMapper.listByIds(task.getFaceSampleIds());
@@ -113,7 +116,7 @@ public class VideoPieceGetter {
                 .stream()
                 .parallel()
                 .forEach(faceSampleList -> {
-                    faceSampleList.forEach(faceSample -> {
+                    faceSampleList.parallelStream().forEach(faceSample -> {
                         DeviceEntity device = deviceRepository.getDevice(faceSample.getDeviceId());
                         DeviceConfigEntity config = deviceRepository.getDeviceConfig(faceSample.getDeviceId());
 
@@ -245,6 +248,9 @@ public class VideoPieceGetter {
                         }
                     });
                 });
+        if (task.faceId != null) {
+            taskStatusBiz.setFaceCutStatus(task.faceId, 1);
+        }
         if (null != task.getCallback()) {
             if (!invoke.get()) {
                 invoke.set(true);
diff --git a/src/main/java/com/ycwl/basic/task/VideoTaskGenerator.java b/src/main/java/com/ycwl/basic/task/VideoTaskGenerator.java
index c0911e6..bd73c15 100644
--- a/src/main/java/com/ycwl/basic/task/VideoTaskGenerator.java
+++ b/src/main/java/com/ycwl/basic/task/VideoTaskGenerator.java
@@ -4,10 +4,14 @@ import cn.hutool.core.date.DateUtil;
 import com.ycwl.basic.biz.TemplateBiz;
 import com.ycwl.basic.mapper.FaceMapper;
 import com.ycwl.basic.mapper.FaceSampleMapper;
+import com.ycwl.basic.mapper.ScenicMapper;
 import com.ycwl.basic.mapper.TemplateMapper;
 import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
 import com.ycwl.basic.model.pc.face.req.FaceReqQuery;
 import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
+import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity;
+import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery;
+import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO;
 import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
 import com.ycwl.basic.repository.ScenicRepository;
 import com.ycwl.basic.repository.TemplateRepository;
@@ -20,6 +24,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
+import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
 
@@ -38,31 +43,71 @@ public class VideoTaskGenerator {
     private TaskTaskServiceImpl taskTaskService;
     @Autowired
     private TemplateMapper templateMapper;
+    @Autowired
+    private ScenicMapper scenicMapper;
+    @Autowired
+    private ScenicRepository scenicRepository;
 
     // TODO: 可配置,现在赶时间暂时写死
 
-    @Scheduled(cron = "0 0 18 * * *")
+    @Scheduled(cron = "0 0 * * * *")
     public void generateVideoTask() {
-        // 指定,获取指定日期的未完成人脸样本,并生成任务
-        Long scenicId = 3946669713328836608L;
-        List<ContentPageVO> contentList = templateMapper.listFor(scenicId);
-        if (contentList.isEmpty()) {
+        List<ScenicRespVO> scenicList = scenicMapper.list(new ScenicReqQuery());
+        if (scenicList.isEmpty()) {
             return;
         }
-        Long templateId = contentList.get(0).getTemplateId();
-        FaceReqQuery query = new FaceReqQuery();
-        query.setScenicId(scenicId);
-        query.setStartTime(DateUtil.beginOfDay(new Date()));
-        query.setEndTime(DateUtil.endOfDay(new Date()));
-        List<FaceRespVO> list = faceMapper.list(query);
-        list.stream().parallel().forEach(face -> {
-            taskFaceService.searchFace(face.getId());
-            boolean canAutoGenerate = templateBiz.determineTemplateCanAutoGenerate(templateId, face.getId(), false);
-            if (canAutoGenerate) {
-                log.info("task callback: 自动生成");
-                taskTaskService.forceCreateTaskByFaceIdAndTempalteId(face.getId(), templateId);
-            } else {
-                log.info("task callback: 不自动生成");
+
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
+        calendar.clear();
+        scenicList.parallelStream().forEach(scenic -> {
+            Long scenicId = scenic.getId();
+            ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(scenicId);
+            if (scenicConfig == null) {
+                log.info("当前景区{},无配置信息", scenic.getName());
+                return;
+            }
+            if (Integer.valueOf(1).equals(scenicConfig.getBookRoutine()) || Integer.valueOf(3).equals(scenicConfig.getBookRoutine())) {
+                Integer hour = scenicConfig.getForceFinishTime();
+                if (hour != currentHour) {
+                    return;
+                }
+                // 定时逻辑
+                List<ContentPageVO> contentList = templateMapper.listFor(scenicId);
+                if (contentList.isEmpty()) {
+                    return;
+                }
+                FaceReqQuery query = new FaceReqQuery();
+                query.setScenicId(scenicId);
+                query.setStartTime(DateUtil.beginOfDay(new Date()));
+                query.setEndTime(DateUtil.endOfDay(new Date()));
+                List<FaceRespVO> list = faceMapper.list(query);
+                list.stream().parallel().forEach(face -> {
+                    taskFaceService.searchFace(face.getId());
+                    if (Integer.valueOf(3).equals(scenicConfig.getBookRoutine())) {
+                        // 全部生成
+                        contentList.forEach(content -> {
+                            Long templateId = content.getTemplateId();
+                            boolean canAutoGenerate = templateBiz.determineTemplateCanAutoGenerate(templateId, face.getId(), false);
+                            if (canAutoGenerate) {
+                                log.info("task callback: 自动生成");
+                                taskTaskService.forceCreateTaskByFaceIdAndTempalteId(face.getId(), templateId);
+                            } else {
+                                log.info("task callback: 不自动生成");
+                            }
+                        });
+                    } else {
+                        Long templateId = contentList.get(0).getTemplateId();
+                        boolean canAutoGenerate = templateBiz.determineTemplateCanAutoGenerate(templateId, face.getId(), false);
+                        if (canAutoGenerate) {
+                            log.info("task callback: 自动生成");
+                            taskTaskService.forceCreateTaskByFaceIdAndTempalteId(face.getId(), templateId);
+                        } else {
+                            log.info("task callback: 不自动生成");
+                        }
+                    }
+                });
             }
         });
     }
diff --git a/src/main/resources/mapper/DeviceMapper.xml b/src/main/resources/mapper/DeviceMapper.xml
index 7870c93..1f79f0b 100644
--- a/src/main/resources/mapper/DeviceMapper.xml
+++ b/src/main/resources/mapper/DeviceMapper.xml
@@ -36,7 +36,8 @@
             online_check = #{onlineCheck},
             online_max_interval = #{onlineMaxInterval},
             cut_pre = #{cutPre},
-            cut_post = #{cutPost}
+            cut_post = #{cutPost},
+            enable_pre_book = #{enablePreBook}
         where id = #{id}
     </update>
     <update id="updateEntity">
diff --git a/src/main/resources/mapper/FaceMapper.xml b/src/main/resources/mapper/FaceMapper.xml
index a86fe57..38f5b85 100644
--- a/src/main/resources/mapper/FaceMapper.xml
+++ b/src/main/resources/mapper/FaceMapper.xml
@@ -70,7 +70,7 @@
         from face
         where id = #{id}
     </select>
-    <select id="getByMemberId" resultType="com.ycwl.basic.model.pc.face.resp.FaceRespVO">
+    <select id="getLatestByMemberId" resultType="com.ycwl.basic.model.pc.face.resp.FaceRespVO">
         select id, scenic_id, member_id, face_url,score, match_sample_ids, first_match_rate, match_result, create_at, update_at
         from face
         where member_id = #{userId} and scenic_id = #{scenicId}
@@ -100,4 +100,11 @@
         where member_id = #{userId} and scenic_id = #{scenicId}
         order by update_at desc
     </select>
+    <select id="findLastFaceByScenicAndUserId" resultType="com.ycwl.basic.model.pc.face.resp.FaceRespVO">
+        select id, scenic_id, member_id, face_url,score, match_sample_ids, first_match_rate, match_result, create_at, update_at
+        from face
+        where member_id = #{userId} and scenic_id = #{scenicId}
+        order by update_at desc
+        limit 1
+    </select>
 </mapper>
diff --git a/src/main/resources/mapper/FaceSampleMapper.xml b/src/main/resources/mapper/FaceSampleMapper.xml
index 71722db..4884d10 100644
--- a/src/main/resources/mapper/FaceSampleMapper.xml
+++ b/src/main/resources/mapper/FaceSampleMapper.xml
@@ -101,7 +101,7 @@
         from face_sample
         where id = #{id}
     </select>
-    <select id="listEntity" resultType="com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity">
+    <select id="listEntityBeforeDate" resultType="com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity">
         select *
         from face_sample
         where scenic_id = #{scenicId} and create_at &lt;= #{endDate}
diff --git a/src/main/resources/mapper/ScenicMapper.xml b/src/main/resources/mapper/ScenicMapper.xml
index 7305cb1..7d02acd 100644
--- a/src/main/resources/mapper/ScenicMapper.xml
+++ b/src/main/resources/mapper/ScenicMapper.xml
@@ -94,7 +94,8 @@
             disable_source_video=#{disableSourceVideo},
             disable_source_image=#{disableSourceImage},
             video_source_store_day=#{videoSourceStoreDay},
-            image_source_store_day=#{imageSourceStoreDay}
+            image_source_store_day=#{imageSourceStoreDay},
+            force_finish_time=#{forceFinishTime}
         </set>
         where id = #{id}
     </update>