指定设备提前预约

This commit is contained in:
Jerry Yan 2025-02-05 14:31:31 +08:00
parent 1eb636d402
commit a016622cc9
27 changed files with 471 additions and 103 deletions

View File

@ -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;
}
}

View File

@ -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")

View File

@ -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

View File

@ -273,7 +273,9 @@ public class ViidController {
faceSampleMapper.add(faceSample);
new Thread(() -> {
taskFaceService.addFaceSample(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());
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);
}
}

View File

@ -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,"扫描特殊标记码"),
;

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -57,4 +57,5 @@ public class DeviceConfigEntity {
* 切割时取人脸后多少秒的视频
*/
private BigDecimal cutPost;
private Integer enablePreBook;
}

View File

@ -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;

View File

@ -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;

View File

@ -43,6 +43,7 @@ public class ScenicConfigEntity {
* 预约流程1-预约2-在线3-全部
*/
private Integer bookRoutine;
private Integer forceFinishTime;
/**
* 样本保存时间
*/

View File

@ -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);
}
}
}, rate, rate, timeUnit);
}
@Override
public void acquire() throws InterruptedException {
synchronized (semaphore) {
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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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 {
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

View File

@ -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);
});

View File

@ -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;
}

View File

@ -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);
}
});
}
}

View File

@ -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);

View File

@ -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,18 +43,41 @@ 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<ScenicRespVO> scenicList = scenicMapper.list(new ScenicReqQuery());
if (scenicList.isEmpty()) {
return;
}
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;
}
Long templateId = contentList.get(0).getTemplateId();
FaceReqQuery query = new FaceReqQuery();
query.setScenicId(scenicId);
query.setStartTime(DateUtil.beginOfDay(new Date()));
@ -57,6 +85,10 @@ public class VideoTaskGenerator {
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: 自动生成");
@ -65,5 +97,18 @@ public class VideoTaskGenerator {
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: 不自动生成");
}
}
});
}
});
}
}

View File

@ -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">

View File

@ -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>

View File

@ -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}

View File

@ -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>