diff --git a/src/main/java/com/ycwl/basic/biz/TemplateBiz.java b/src/main/java/com/ycwl/basic/biz/TemplateBiz.java index 4a3740f..62a97df 100644 --- a/src/main/java/com/ycwl/basic/biz/TemplateBiz.java +++ b/src/main/java/com/ycwl/basic/biz/TemplateBiz.java @@ -1,8 +1,10 @@ package com.ycwl.basic.biz; +import com.ycwl.basic.mapper.SourceMapper; import com.ycwl.basic.model.pc.face.resp.FaceRespVO; import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity; import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO; +import com.ycwl.basic.model.pc.source.entity.SourceEntity; import com.ycwl.basic.model.pc.template.entity.TemplateConfigEntity; import com.ycwl.basic.repository.FaceRepository; import com.ycwl.basic.repository.TemplateRepository; @@ -20,6 +22,8 @@ public class TemplateBiz { private TemplateRepository templateRepository; @Autowired private FaceRepository faceRepository; + @Autowired + private SourceMapper sourceMapper; public boolean determineTemplateCanGenerate(Long templateId, Long faceId) { @@ -35,7 +39,12 @@ public class TemplateBiz { return true; } List faceSampleList = faceRepository.getFaceSampleList(faceId); - long count = faceSampleList.stream().map(FaceSampleEntity::getDeviceId).filter(deviceId -> placeholderList.contains(deviceId.toString())).count(); + if (faceSampleList.isEmpty()) { + return false; + } + // todo fix me + List sourceEntities = sourceMapper.listVideoBySampleIds(faceSampleList.stream().map(FaceSampleEntity::getId).collect(Collectors.toList())); + long count = sourceEntities.stream().map(SourceEntity::getDeviceId).filter(deviceId -> placeholderList.contains(deviceId.toString())).count(); return count >= minimalPlaceholderFill; } diff --git a/src/main/java/com/ycwl/basic/controller/mobile/manage/AppScenicAccountController.java b/src/main/java/com/ycwl/basic/controller/mobile/manage/AppScenicAccountController.java index 08591f8..cbbc58c 100644 --- a/src/main/java/com/ycwl/basic/controller/mobile/manage/AppScenicAccountController.java +++ b/src/main/java/com/ycwl/basic/controller/mobile/manage/AppScenicAccountController.java @@ -4,6 +4,7 @@ import com.ycwl.basic.annotation.IgnoreToken; import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginReq; import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginRespVO; import com.ycwl.basic.model.mobile.weChat.DTO.WeChatUserInfoDTO; +import com.ycwl.basic.model.pc.device.resp.DeviceRespVO; import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO; import com.ycwl.basic.service.mobile.AppScenicService; import com.ycwl.basic.utils.ApiResponse; @@ -17,6 +18,8 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + /** * @Author:longbinbin * @Date:2024/12/12 18:28 @@ -40,4 +43,9 @@ public class AppScenicAccountController { public ApiResponse getMyScenic() { return scenicService.getMyScenic(); } + + @GetMapping("/devices") + public ApiResponse> getDeviceList() { + return scenicService.getMyDevices(); + } } 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 d746442..a909501 100644 --- a/src/main/java/com/ycwl/basic/controller/viid/ViidController.java +++ b/src/main/java/com/ycwl/basic/controller/viid/ViidController.java @@ -28,6 +28,7 @@ import com.ycwl.basic.model.viid.resp.VIIDBaseResp; import com.ycwl.basic.service.task.TaskFaceService; import com.ycwl.basic.storage.StorageFactory; import com.ycwl.basic.storage.adapters.IStorageAdapter; +import com.ycwl.basic.task.DynamicTaskGenerator; import com.ycwl.basic.utils.ImageUtils; import com.ycwl.basic.utils.IpUtils; import com.ycwl.basic.utils.SnowFlakeUtil; @@ -243,7 +244,8 @@ public class ViidController { String url = adapter.uploadFile(file, "user-face", UUID.randomUUID() + "." + ext); faceSample.setFaceUrl(url); faceSampleMapper.add(faceSample); - log.info("人脸信息入库成功!"); + log.info("人脸信息入库成功!设备ID:{}", deviceID); + DynamicTaskGenerator.addTask(faceSample.getId()); taskFaceService.addFaceSample(faceSample.getId()); } // Type=14 场景图 @@ -262,10 +264,10 @@ public class ViidController { source.setUrl(url); source.setPosJson(JSON.toJSONString(facePosition)); sourceMapper.add(source); + log.info("源照片入库成功!设备ID:{}", deviceID); } } } - log.info("设备ID:{}", deviceID); } return new VIIDBaseResp( diff --git a/src/main/java/com/ycwl/basic/interceptor/AuthInterceptor.java b/src/main/java/com/ycwl/basic/interceptor/AuthInterceptor.java index 6bc6cb2..16d82e1 100644 --- a/src/main/java/com/ycwl/basic/interceptor/AuthInterceptor.java +++ b/src/main/java/com/ycwl/basic/interceptor/AuthInterceptor.java @@ -75,10 +75,10 @@ public class AuthInterceptor extends HandlerInterceptorAdapter { JwtInfo jwtInfo; try { jwtInfo = JwtTokenUtil.parsingToken(token); - LocalDateTime expireTime = jwtInfo.getExpireTime(); - if (LocalDateTime.now(ZoneId.systemDefault()).isAfter(expireTime)) { - throw new TokenExpireException("token过期"); - } +// LocalDateTime expireTime = jwtInfo.getExpireTime(); +// if (LocalDateTime.now(ZoneId.systemDefault()).isAfter(expireTime)) { +// throw new TokenExpireException("token过期"); +// } BaseContextHandler.setToken(token); BaseContextHandler.setName(jwtInfo.getName()); BaseContextHandler.setUserId(String.valueOf(jwtInfo.getUserId())); diff --git a/src/main/java/com/ycwl/basic/mapper/DeviceMapper.java b/src/main/java/com/ycwl/basic/mapper/DeviceMapper.java index 646aacd..c086694 100644 --- a/src/main/java/com/ycwl/basic/mapper/DeviceMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/DeviceMapper.java @@ -19,6 +19,7 @@ import java.util.List; @Mapper public interface DeviceMapper { List list(DeviceReqQuery deviceReqQuery); + List listAll(); DeviceRespVO getById(Long id); int add(DeviceAddOrUpdateReq deviceReqQuery); int deleteById(Long id); @@ -26,7 +27,7 @@ public interface DeviceMapper { int updateStatus(Long id); DeviceEntity getByDeviceId(Long deviceId); - List listByScenicId(Long scenicId); + List listByScenicIdWithWVP(Long scenicId); ScenicDeviceCountVO deviceCountByScenicId(@Param("scenicId") Long scenicId,@Param("userId") Long userId); diff --git a/src/main/java/com/ycwl/basic/mapper/VideoMapper.java b/src/main/java/com/ycwl/basic/mapper/VideoMapper.java index e84dcf3..851571c 100644 --- a/src/main/java/com/ycwl/basic/mapper/VideoMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/VideoMapper.java @@ -8,6 +8,7 @@ import com.ycwl.basic.model.pc.video.resp.VideoRespVO; import lombok.NonNull; import org.apache.ibatis.annotations.Mapper; +import java.util.Date; import java.util.List; /** @@ -45,4 +46,6 @@ public interface VideoMapper { MemberVideoEntity queryUserVideo(Long userId, Long videoId); int updateRelationWhenTaskSuccess(Long taskId, Long videoId, int isBuy); + + List listRelationByCreateTime(Date startTime, Date endTime); } diff --git a/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceEntity.java b/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceEntity.java index 054f256..684bdd6 100644 --- a/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceEntity.java +++ b/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceEntity.java @@ -28,6 +28,7 @@ public class DeviceEntity { * 设备编号 */ private String no; + private String no2; /** * 经度 */ diff --git a/src/main/java/com/ycwl/basic/model/pc/device/resp/DeviceRespVO.java b/src/main/java/com/ycwl/basic/model/pc/device/resp/DeviceRespVO.java index a56bf7a..350d440 100644 --- a/src/main/java/com/ycwl/basic/model/pc/device/resp/DeviceRespVO.java +++ b/src/main/java/com/ycwl/basic/model/pc/device/resp/DeviceRespVO.java @@ -39,4 +39,6 @@ public class DeviceRespVO { private String scenicName; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date keepaliveAt; + private String deviceNo; + private String channelNo; } diff --git a/src/main/java/com/ycwl/basic/model/pc/video/entity/MemberVideoEntity.java b/src/main/java/com/ycwl/basic/model/pc/video/entity/MemberVideoEntity.java index b13dbdd..0205567 100644 --- a/src/main/java/com/ycwl/basic/model/pc/video/entity/MemberVideoEntity.java +++ b/src/main/java/com/ycwl/basic/model/pc/video/entity/MemberVideoEntity.java @@ -2,6 +2,8 @@ package com.ycwl.basic.model.pc.video.entity; import lombok.Data; +import java.util.Date; + @Data public class MemberVideoEntity { private Long id; @@ -13,4 +15,5 @@ public class MemberVideoEntity { private Long videoId; private Integer isBuy; private Long orderId; + private Date createTime; } diff --git a/src/main/java/com/ycwl/basic/repository/FaceRepository.java b/src/main/java/com/ycwl/basic/repository/FaceRepository.java index 46d9432..e24a618 100644 --- a/src/main/java/com/ycwl/basic/repository/FaceRepository.java +++ b/src/main/java/com/ycwl/basic/repository/FaceRepository.java @@ -39,9 +39,9 @@ public class FaceRepository { } public List getFaceSampleList(Long faceId) { - if (redisTemplate.hasKey(String.format(FACE_SAMPLE_CACHE_KEY, faceId))) { - return JSONObject.parseArray(redisTemplate.opsForValue().get(String.format(FACE_SAMPLE_CACHE_KEY, faceId)), FaceSampleEntity.class); - } +// if (redisTemplate.hasKey(String.format(FACE_SAMPLE_CACHE_KEY, faceId))) { +// return JSONObject.parseArray(redisTemplate.opsForValue().get(String.format(FACE_SAMPLE_CACHE_KEY, faceId)), FaceSampleEntity.class); +// } FaceEntity face = getFace(faceId); if (face == null) { return Collections.emptyList(); @@ -50,9 +50,9 @@ public class FaceRepository { return Collections.emptyList(); } List list = faceSampleMapper.listByIds(Arrays.stream(face.getMatchSampleIds().split(",")).map(Long::valueOf).collect(Collectors.toList())); - if (!list.isEmpty()) { - redisTemplate.opsForValue().set(String.format(FACE_SAMPLE_CACHE_KEY, faceId), JSONObject.toJSONString(list)); - } +// if (!list.isEmpty()) { +// redisTemplate.opsForValue().set(String.format(FACE_SAMPLE_CACHE_KEY, faceId), JSONObject.toJSONString(list)); +// } return list; } diff --git a/src/main/java/com/ycwl/basic/service/impl/mobile/AppScenicServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/mobile/AppScenicServiceImpl.java index bf07b8a..2ada2da 100644 --- a/src/main/java/com/ycwl/basic/service/impl/mobile/AppScenicServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/mobile/AppScenicServiceImpl.java @@ -8,7 +8,6 @@ import com.ycwl.basic.biz.TemplateBiz; import com.ycwl.basic.constant.BaseContextHandler; import com.ycwl.basic.mapper.*; import com.ycwl.basic.model.jwt.JwtInfo; -import com.ycwl.basic.model.mobile.goods.GoodsPageVO; import com.ycwl.basic.model.mobile.order.IsBuyRespVO; import com.ycwl.basic.model.mobile.scenic.ScenicAppVO; import com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO; @@ -16,6 +15,7 @@ import com.ycwl.basic.model.mobile.scenic.ScenicIndexVO; import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginReq; import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginRespVO; import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO; +import com.ycwl.basic.model.pc.device.resp.DeviceRespVO; import com.ycwl.basic.model.pc.face.resp.FaceRespVO; import com.ycwl.basic.model.pc.scenic.entity.ScenicAccountEntity; import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity; @@ -31,7 +31,6 @@ import com.ycwl.basic.repository.TemplateRepository; import com.ycwl.basic.utils.ApiResponse; import com.ycwl.basic.utils.JwtTokenUtil; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -39,6 +38,7 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -58,8 +58,6 @@ public class AppScenicServiceImpl implements AppScenicService { @Autowired private SourceMapper sourceMapper; @Autowired - private TaskMapper taskMapper; - @Autowired private VideoMapper videoMapper; @Autowired private TemplateMapper templateMapper; @@ -67,11 +65,6 @@ public class AppScenicServiceImpl implements AppScenicService { private ScenicAccountMapper scenicAccountMapper; @Autowired private JwtTokenUtil jwtTokenUtil; - - @Value("${face.score}") - private BigDecimal faceScore; - @Autowired - private TemplateRepository templateRepository; @Autowired private OrderBiz orderBiz; @Autowired @@ -114,9 +107,6 @@ public class AppScenicServiceImpl implements AppScenicService { if (faceRespVO == null) { return ApiResponse.success(new ArrayList<>()); } - if (StringUtils.isBlank(faceRespVO.getMatchSampleIds())) { - return ApiResponse.success(new ArrayList<>()); - } List contentList = templateMapper.listFor(faceRespVO.getScenicId()); contentList.forEach(contentPageVO -> { List memberVideoEntityList = videoMapper.userFaceTemplateVideo(userId, faceId, contentPageVO.getTemplateId()); @@ -125,8 +115,13 @@ public class AppScenicServiceImpl implements AppScenicService { contentPageVO.setIsBuy(memberVideoEntityList.get(0).getIsBuy()); contentPageVO.setContentId(memberVideoEntityList.get(0).getVideoId()); VideoRespVO videoMapperById = videoMapper.getById(contentPageVO.getContentId()); - contentPageVO.setDuration(videoMapperById.getDuration()); - contentPageVO.setLockType(-1); + if (videoMapperById != null) { + contentPageVO.setDuration(videoMapperById.getDuration()); + contentPageVO.setLockType(-1); + } else { + contentPageVO.setLockType(0); + contentPageVO.setContentType(0); + } } else { contentPageVO.setContentType(0); boolean canGenerate = templateBiz.determineTemplateCanGenerate(contentPageVO.getTemplateId(), faceId); @@ -234,4 +229,15 @@ public class AppScenicServiceImpl implements AppScenicService { } return getDetails(account.getScenicId()); } + + @Override + public ApiResponse> getMyDevices() { + String userId = BaseContextHandler.getUserId(); + ScenicAccountEntity account = scenicAccountMapper.findAccountById(userId); + if (account == null) { + return ApiResponse.fail("用户未绑定景区"); + } + List deviceRespVOList = deviceMapper.listByScenicIdWithWVP(account.getScenicId()); + return ApiResponse.success(deviceRespVOList); + } } diff --git a/src/main/java/com/ycwl/basic/service/impl/pc/FaceServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/pc/FaceServiceImpl.java index 38959a1..6b8fcec 100644 --- a/src/main/java/com/ycwl/basic/service/impl/pc/FaceServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/pc/FaceServiceImpl.java @@ -37,6 +37,7 @@ import org.springframework.web.multipart.MultipartFile; import java.math.BigDecimal; import java.util.Date; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -56,9 +57,6 @@ public class FaceServiceImpl implements FaceService { @Autowired private StatisticsMapper statisticsMapper; - @Value("${face.score}") - private float faceScore; - private final float strictScore = 0.75F; @Autowired private TaskService taskTaskService; @Autowired @@ -136,18 +134,13 @@ public class FaceServiceImpl implements FaceService { adapter.deleteFile(filePath, fileName); throw new BaseException("人脸照片校验失败,请重新上传"); } - float score = scenicDbSearchResult.getScore(); - if (score faceAny = userDbSearchResult.getSampleListIds().stream().filter(oldFaceId -> { + FaceEntity face = faceRepository.getFace(oldFaceId); + if (face == null) { + return false; + } + return face.getScenicId().equals(scenicId); + }).findAny(); + if (faceAny.isPresent()) { + Long oldFaceId = faceAny.get(); + FaceRespVO oldFace = faceMapper.getById(oldFaceId); + if (oldFace == null) { + faceService.deleteFaceSample(USER_FACE_DB_NAME, oldFaceId.toString()); + } else { + faceEntity.setId(oldFaceId); + } } } if (scenicDbSearchResult.getFirstMatchRate() != null) { @@ -215,13 +217,15 @@ public class FaceServiceImpl implements FaceService { } return memberSourceEntity; }).collect(Collectors.toList()); - sourceMapper.addRelations(memberSourceEntityList); - taskTaskService.autoCreateTaskByFaceId(faceEntity.getId()); - VideoPieceGetter.Task task = new VideoPieceGetter.Task(); - task.faceId = faceEntity.getId(); - task.faceSampleIds = sampleListIds; - task.memberId = userId; - VideoPieceGetter.addTask(task); + if (!memberSourceEntityList.isEmpty()) { + sourceMapper.addRelations(memberSourceEntityList); + taskTaskService.autoCreateTaskByFaceId(faceEntity.getId()); + VideoPieceGetter.Task task = new VideoPieceGetter.Task(); + task.faceId = faceEntity.getId(); + task.faceSampleIds = sampleListIds; + task.memberId = userId; + VideoPieceGetter.addTask(task); + } } return ApiResponse.success(resp); } diff --git a/src/main/java/com/ycwl/basic/service/mobile/AppScenicService.java b/src/main/java/com/ycwl/basic/service/mobile/AppScenicService.java index e651319..abe3ccc 100644 --- a/src/main/java/com/ycwl/basic/service/mobile/AppScenicService.java +++ b/src/main/java/com/ycwl/basic/service/mobile/AppScenicService.java @@ -7,6 +7,7 @@ import com.ycwl.basic.model.mobile.scenic.ScenicIndexVO; import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginReq; import com.ycwl.basic.model.mobile.scenic.account.ScenicLoginRespVO; import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO; +import com.ycwl.basic.model.pc.device.resp.DeviceRespVO; import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery; import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO; import com.ycwl.basic.utils.ApiResponse; @@ -40,4 +41,6 @@ public interface AppScenicService { ApiResponse> contentListUseDefaultFace(); ApiResponse getMyScenic(); + + ApiResponse> getMyDevices(); } 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 0fe09c2..6f58a53 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 @@ -112,7 +112,7 @@ public class TaskFaceServiceImpl implements TaskFaceService { request.setDbName(dbName); request.setImageUrl(faceUrl); request.setLimit(100); - request.setQualityScoreThreshold(60f); +// request.setQualityScoreThreshold(60f); FaceDetectLog log = FaceDetectLog.quickCreate("预留字段", request); try { SearchFaceResponse response = client.getAcsResponse(request); 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 ab3f316..ccfc4e1 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 @@ -183,6 +183,10 @@ public class TaskTaskServiceImpl implements TaskService { List sourceEntityList = sourceMapper.listVideoByScenicFaceRelation(scenicId, faceId); Map> sourcesMap = sourceEntityList.stream() .collect(Collectors.groupingBy(item -> item.getDeviceId().toString())); + sourcesMap.forEach((key, value) -> { + // 每个value只保留第一个 + value.removeIf(item -> !value.get(0).equals(item)); + }); TaskEntity taskEntity = new TaskEntity(); taskEntity.setId(SnowFlakeUtil.getLongId()); taskEntity.setFaceId(faceId); @@ -227,13 +231,16 @@ public class TaskTaskServiceImpl implements TaskService { public void autoCreateTaskByFaceId(Long faceId) { FaceRespVO faceRespVO = faceMapper.getById(faceId); if (faceRespVO == null) { + log.info("faceId:{} is not exist", faceId); return; } if (!StringUtils.isNotBlank(faceRespVO.getMatchSampleIds())) { + log.info("faceId:{} matchSampleIds is empty", faceId); return; } List faceSampleList = faceSampleMapper.listByIds(Arrays.stream(faceRespVO.getMatchSampleIds().split(",")).map(Long::valueOf).collect(Collectors.toList())); if (faceSampleList.isEmpty()) { + log.info("faceId:{} faceSampleList is empty", faceId); return; } ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(faceRespVO.getScenicId()); @@ -270,39 +277,39 @@ public class TaskTaskServiceImpl implements TaskService { if (sourceList.isEmpty()) { return; } - if (automatic > 0) { - TaskReqQuery taskReqQuery = new TaskReqQuery(); - taskReqQuery.setFaceId(faceId); - taskReqQuery.setTemplateId(templateId); - List list = taskMapper.list(taskReqQuery); - if (!list.isEmpty()) { - list.parallelStream().forEach(task -> { - MemberVideoEntity memberVideo = videoMapper.queryRelationByMemberTask(faceRespVO.getMemberId(), task.getId()); - if (memberVideo == null) { - memberVideo = new MemberVideoEntity(); - memberVideo.setMemberId(faceRespVO.getMemberId()); - memberVideo.setScenicId(task.getScenicId()); - memberVideo.setFaceId(faceId); - memberVideo.setTemplateId(task.getTemplateId()); - if (Integer.valueOf(1).equals(scenicConfig.getAllFree())) { - memberVideo.setIsBuy(1); - } else { - memberVideo.setIsBuy(0); - } - memberVideo.setTaskId(task.getId()); - VideoEntity video = videoMapper.findByTaskId(task.getId()); - if (video != null) { - memberVideo.setVideoId(video.getId()); - } - videoMapper.addRelation(memberVideo); - new Thread(() -> { - sendVideoGeneratedServiceNotification(list.get(0).getId(), faceRespVO.getMemberId()); - }).start(); - } - }); - return; - } - } +// if (automatic > 0) { +// TaskReqQuery taskReqQuery = new TaskReqQuery(); +// taskReqQuery.setFaceId(faceId); +// taskReqQuery.setTemplateId(templateId); +// List list = taskMapper.list(taskReqQuery); +// if (!list.isEmpty()) { +// list.parallelStream().forEach(task -> { +// MemberVideoEntity memberVideo = videoMapper.queryRelationByMemberTask(faceRespVO.getMemberId(), task.getId()); +// if (memberVideo == null) { +// memberVideo = new MemberVideoEntity(); +// memberVideo.setMemberId(faceRespVO.getMemberId()); +// memberVideo.setScenicId(task.getScenicId()); +// memberVideo.setFaceId(faceId); +// memberVideo.setTemplateId(task.getTemplateId()); +// if (Integer.valueOf(1).equals(scenicConfig.getAllFree())) { +// memberVideo.setIsBuy(1); +// } else { +// memberVideo.setIsBuy(0); +// } +// memberVideo.setTaskId(task.getId()); +// VideoEntity video = videoMapper.findByTaskId(task.getId()); +// if (video != null) { +// memberVideo.setVideoId(video.getId()); +// } +// videoMapper.addRelation(memberVideo); +// new Thread(() -> { +// sendVideoGeneratedServiceNotification(list.get(0).getId(), faceRespVO.getMemberId()); +// }).start(); +// } +// }); +// return; +// } +// } VideoPieceGetter.Task task = new VideoPieceGetter.Task(); task.faceId = faceId; task.faceSampleIds = faceSampleIds; @@ -319,6 +326,10 @@ public class TaskTaskServiceImpl implements TaskService { Map> sourcesMap = videoSourceList.stream() .peek(item -> item.setUrl(item.getVideoUrl())) .collect(Collectors.groupingBy(item -> item.getDeviceId().toString())); + sourcesMap.forEach((key, value) -> { + // 每个value只保留第一个 + value.removeIf(item -> !value.get(0).equals(item)); + }); TaskReqQuery taskReqQuery = new TaskReqQuery(); taskReqQuery.setFaceId(faceId); taskReqQuery.setTemplateId(templateId); @@ -332,6 +343,7 @@ public class TaskTaskServiceImpl implements TaskService { memberVideoEntity.setTemplateId(templateId); memberVideoEntity.setIsBuy(0); if (list.isEmpty()) { + log.info("创建任务! faceId:{},templateId:{},taskParams:{}", faceId, templateId, sourcesMap); TaskEntity taskEntity = new TaskEntity(); taskEntity.setId(SnowFlakeUtil.getLongId()); taskEntity.setScenicId(faceRespVO.getScenicId()); @@ -343,6 +355,7 @@ public class TaskTaskServiceImpl implements TaskService { taskMapper.add(taskEntity); memberVideoEntity.setTaskId(taskEntity.getId()); } else { + log.info("重复task! faceId:{},templateId:{},taskParams:{}", faceId, templateId, sourcesMap); memberVideoEntity.setTaskId(list.get(0).getId()); VideoEntity video = videoMapper.findByTaskId(list.get(0).getId()); if (video != null) { diff --git a/src/main/java/com/ycwl/basic/task/DownloadNotificationTasker.java b/src/main/java/com/ycwl/basic/task/DownloadNotificationTasker.java new file mode 100644 index 0000000..ea9675e --- /dev/null +++ b/src/main/java/com/ycwl/basic/task/DownloadNotificationTasker.java @@ -0,0 +1,126 @@ +package com.ycwl.basic.task; + +import cn.hutool.core.date.DateUtil; +import com.ycwl.basic.mapper.MemberMapper; +import com.ycwl.basic.mapper.VideoMapper; +import com.ycwl.basic.model.pc.member.resp.MemberRespVO; +import com.ycwl.basic.model.pc.mp.MpConfigEntity; +import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity; +import com.ycwl.basic.model.pc.scenic.entity.ScenicEntity; +import com.ycwl.basic.notify.NotifyFactory; +import com.ycwl.basic.notify.adapters.INotifyAdapter; +import com.ycwl.basic.notify.entity.NotifyContent; +import com.ycwl.basic.notify.enums.NotifyType; +import com.ycwl.basic.repository.ScenicRepository; +import com.ycwl.basic.repository.TemplateRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +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.Date; +import java.util.HashMap; +import java.util.Map; + +@Component +@EnableScheduling +@Slf4j +public class DownloadNotificationTasker { + @Autowired + private ScenicRepository scenicRepository; + @Autowired + private VideoMapper videoMapper; + @Autowired + private MemberMapper memberMapper; + + @Scheduled(cron = "0 0 21 * * *") + public void sendDownloadNotification() { + log.info("开始执行定时任务"); + videoMapper.listRelationByCreateTime(new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000), new Date()) + .forEach(item -> { + if (item.getIsBuy() == 1) { + return; + } + MemberRespVO member = memberMapper.getById(item.getMemberId()); + MpConfigEntity scenicMp = scenicRepository.getScenicMpConfig(member.getScenicId()); + // 发送模板消息 + String templateId = scenicRepository.getVideoDownloadTemplateId(item.getScenicId()); + if (StringUtils.isBlank(templateId)) { + log.info("模板消息为空"); + return; + } + log.info("发送模板消息"); + ScenicEntity scenic = scenicRepository.getScenic(item.getScenicId()); + String title = "您在【" + scenic.getName() + "】的专属影像"; + String page = "pages/videoSynthesis/buy?scenicId=" + item.getScenicId() + "&faceId=" + item.getFaceId() + "&id=" + item.getVideoId(); + /** + * 景区 {{thing1.DATA}} + * 备注 {{thing3.DATA}} + */ + Map params = new HashMap<>(); + Map dataParam = new HashMap<>(); + Map videoMap = new HashMap<>(); + videoMap.put("value", title); + dataParam.put("thing1", videoMap); + Map remarkMap = new HashMap<>(); + remarkMap.put("value", "系统删除前,请下载好您的旅行视频"); + dataParam.put("thing3", remarkMap); + params.put("data", dataParam); + params.put("page", page); + params.put("template_id", templateId); + log.info("视频下载通知模板参数:{},用户ID:{}", params, member.getOpenId()); + INotifyAdapter adapter = NotifyFactory.get(NotifyType.WX_MP_SRV, scenicMp.toMap()); + adapter.sendTo(new NotifyContent(title, page, params), member.getOpenId()); + }); + } + + @Scheduled(cron = "0 0 20 * * *") + public void sendExpireNotification() { + log.info("开始执行定时任务"); + videoMapper.listRelationByCreateTime(new Date(System.currentTimeMillis() - 3 * 24 * 60 * 60 * 1000), new Date(System.currentTimeMillis() - 2 * 24 * 60 * 60 * 1000)) + .forEach(item -> { + if (item.getIsBuy() == 1) { + return; + } + MemberRespVO member = memberMapper.getById(item.getMemberId()); + MpConfigEntity scenicMp = scenicRepository.getScenicMpConfig(member.getScenicId()); + ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(item.getScenicId()); + Integer videoStoreDay = scenicConfig.getVideoStoreDay(); + // 发送模板消息 + String templateId = scenicRepository.getVideoPreExpireTemplateId(item.getScenicId()); + if (StringUtils.isBlank(templateId)) { + log.info("模板消息为空"); + return; + } + log.info("发送模板消息"); + ScenicEntity scenic = scenicRepository.getScenic(item.getScenicId()); + String title = "您在【" + scenic.getName() + "】的专属影像"; + String page = "pages/videoSynthesis/buy?scenicId=" + item.getScenicId() + "&faceId=" + item.getFaceId() + "&id=" + item.getVideoId(); + /** + * 影像名称 {{thing1.DATA}} + * 过期时间 {{time2.DATA}} + * 备注 {{thing3.DATA}} + */ + Map params = new HashMap<>(); + Map dataParam = new HashMap<>(); + Map videoMap = new HashMap<>(); + videoMap.put("value", title); + dataParam.put("thing1", videoMap); + Map dateMap = new HashMap<>(); + Date expireDate = new Date(item.getCreateTime().getTime() + videoStoreDay * 24 * 60 * 60 * 1000); + dateMap.put("value", DateUtil.format(expireDate, "yyyy-MM-dd HH:mm")); + dataParam.put("time2", dateMap); + Map remarkMap = new HashMap<>(); + remarkMap.put("value", "视频即将删除,花点小钱买下回忆"); + dataParam.put("thing3", remarkMap); + params.put("data", dataParam); + params.put("page", page); + params.put("template_id", templateId); + log.info("视频下载通知模板参数:{},用户ID:{}", params, member.getOpenId()); + INotifyAdapter adapter = NotifyFactory.get(NotifyType.WX_MP_SRV, scenicMp.toMap()); + adapter.sendTo(new NotifyContent(title, page, params), member.getOpenId()); + }); + } +} diff --git a/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java b/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java index ce4dcbb..7c3bf74 100644 --- a/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java +++ b/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java @@ -1,13 +1,12 @@ package com.ycwl.basic.task; import com.ycwl.basic.biz.TemplateBiz; -import com.ycwl.basic.mapper.DeviceMapper; 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.pc.face.entity.FaceEntity; import com.ycwl.basic.model.pc.face.resp.FaceRespVO; -import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity; 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; @@ -16,8 +15,13 @@ import com.ycwl.basic.model.pc.template.entity.TemplateConfigEntity; import com.ycwl.basic.model.pc.template.req.TemplateReqQuery; import com.ycwl.basic.model.pc.template.resp.TemplateRespVO; import com.ycwl.basic.model.task.resp.SearchFaceRespVo; +import com.ycwl.basic.repository.FaceRepository; +import com.ycwl.basic.repository.ScenicRepository; +import com.ycwl.basic.repository.TemplateRepository; import com.ycwl.basic.service.task.TaskFaceService; import com.ycwl.basic.service.task.TaskService; +import lombok.AllArgsConstructor; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableScheduling; @@ -28,6 +32,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.ycwl.basic.constant.FaceConstant.USER_FACE_DB_NAME; @Component @EnableScheduling @@ -45,6 +55,14 @@ public class DynamicTaskGenerator { private TaskService taskService; @Autowired private TemplateBiz templateBiz; + @Autowired + private FaceSampleMapper faceSampleMapper; + @Autowired + private ScenicRepository scenicRepository; + @Autowired + private TemplateRepository templateRepository; + @Autowired + private FaceRepository faceRepository; @Scheduled(cron = "0 0 * * * ?") public void dynamicTask() { @@ -113,4 +131,83 @@ public class DynamicTaskGenerator { } } } + + + @Data + @AllArgsConstructor + public static class Task implements Delayed { + public Long faceSampleId; + public Date startTime; + + @Override + public long getDelay(TimeUnit unit) { + return unit.convert(startTime.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + + @Override + public int compareTo(Delayed o) { + return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS)); + } + } + + public static DelayQueue queue = new DelayQueue<>(); + + public static void addTask(Long faceSampleId) { + Date createTime = new Date(); + // 两分钟后 + createTime.setTime(createTime.getTime() + 120000L); + queue.add(new Task(faceSampleId, createTime)); + } + + @Scheduled(fixedRate = 30000L) + public void doTask() { + Task task = queue.poll(); + if (task == null) { + return; + } + log.info("开始执行任务:{}", task); + // 根据人脸照片获取人脸样本ID + FaceSampleRespVO faceSample = faceSampleMapper.getById(task.getFaceSampleId()); + if (faceSample == null) { + log.info("人脸样本ID{}不存在", task.getFaceSampleId()); + return; + } + ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(faceSample.getScenicId()); + if (scenicConfig == null) { + log.info("当前景区{},无配置", faceSample.getScenicId()); + return; + } + if (!Integer.valueOf(1).equals(scenicConfig.getBookRoutine()) && !Integer.valueOf(3).equals(scenicConfig.getBookRoutine())) { + log.info("当前景区{}未启用预约流程,跳过", faceSample.getScenicId()); + return; + } + SearchFaceRespVo userDbSearchResult = faceService.searchFace(USER_FACE_DB_NAME, faceSample.getFaceUrl()); + // 如果人脸样本ID在人脸样本库中,则创建任务 + if (!userDbSearchResult.getSampleListIds().isEmpty()) { + log.info("人脸样本ID在人脸样本库中,创建任务:{}", task); + // 找 + List faceIdList = userDbSearchResult.getSampleListIds().stream().filter(faceId -> { + FaceEntity face = faceRepository.getFace(faceId); + if (face == null) { + return false; + } + return face.getScenicId().equals(faceSample.getScenicId()); + }).collect(Collectors.toList()); + if (faceIdList.isEmpty()) { + log.info("本景区人脸样本ID不在人脸样本库中,忽略任务:{}", task); + return; + } + List templateList = templateRepository.getTemplateListByScenicId(faceSample.getScenicId()); + if (templateList == null || templateList.isEmpty()) { + log.info("当前景区{},无模板配置", faceSample.getScenicId()); + return; + } + faceIdList.forEach(faceId -> { + log.info("自动下发任务,人脸ID:{}", faceId); + taskService.autoCreateTaskByFaceId(faceId); + }); + } else { + log.info("人脸样本ID不在人脸样本库中,忽略任务:{}", task); + } + } } diff --git a/src/main/java/com/ycwl/basic/task/VideoPieceCleaner.java b/src/main/java/com/ycwl/basic/task/VideoPieceCleaner.java index 8ba2506..a1a9041 100644 --- a/src/main/java/com/ycwl/basic/task/VideoPieceCleaner.java +++ b/src/main/java/com/ycwl/basic/task/VideoPieceCleaner.java @@ -5,6 +5,7 @@ import com.ycwl.basic.device.DeviceFactory; import com.ycwl.basic.device.operator.IDeviceStorageOperator; import com.ycwl.basic.mapper.DeviceMapper; import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; +import com.ycwl.basic.model.pc.device.entity.DeviceEntity; import com.ycwl.basic.model.pc.device.req.DeviceReqQuery; import com.ycwl.basic.model.pc.device.resp.DeviceRespVO; import lombok.extern.slf4j.Slf4j; @@ -26,8 +27,8 @@ public class VideoPieceCleaner { @Scheduled(cron = "0 0 0 * * ?") public void clean() { log.info("开始删除视频文件"); - List deviceList = deviceMapper.list(new DeviceReqQuery()); - for (DeviceRespVO device : deviceList) { + List deviceList = deviceMapper.listAll(); + for (DeviceEntity device : deviceList) { DeviceConfigEntity config = deviceMapper.getConfigByDeviceId(device.getId()); if (config == null) { continue; @@ -38,7 +39,7 @@ public class VideoPieceCleaner { if (config.getStoreExpireDay() <= 0) { continue; } - IDeviceStorageOperator storageOperator = DeviceFactory.getDeviceStorageOperator(null, config); + IDeviceStorageOperator storageOperator = DeviceFactory.getDeviceStorageOperator(device, config); if (storageOperator == null) { continue; } diff --git a/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java b/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java index bc01c82..12a7c59 100644 --- a/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java +++ b/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java @@ -146,23 +146,33 @@ public class VideoPieceGetter { return; } log.info("查询到可用的文件: {}", listByDtRange); - long offset = faceSample.getCreateAt().getTime() - cutPre.multiply(BigDecimal.valueOf(1000)).longValue() - listByDtRange.get(0).getCreateTime().getTime(); - FfmpegTask ffmpegTask = new FfmpegTask(); - ffmpegTask.setFileList(listByDtRange); - ffmpegTask.setDuration(duration); - ffmpegTask.setOffsetStart(BigDecimal.valueOf(offset, 3)); - File outFile = new File(faceSample.getDeviceId().toString() + "_" + faceSample.getId() + ".mp4"); - ffmpegTask.setOutputFile(outFile.getAbsolutePath()); - boolean result = startFfmpegTask(ffmpegTask); - if (!result) { - log.warn("视频裁切失败"); - return; + // 如果完全一致,就不需要裁切 + String url; + if ( + listByDtRange.size() != 1 || + (listByDtRange.get(0).getCreateTime().getTime() != faceSample.getCreateAt().getTime() - cutPre.multiply(BigDecimal.valueOf(1000)).longValue() + || listByDtRange.get(0).getEndTime().getTime() != faceSample.getCreateAt().getTime() + cutPost.multiply(BigDecimal.valueOf(1000)).longValue()) + ) { + long offset = faceSample.getCreateAt().getTime() - cutPre.multiply(BigDecimal.valueOf(1000)).longValue() - listByDtRange.get(0).getCreateTime().getTime(); + FfmpegTask ffmpegTask = new FfmpegTask(); + ffmpegTask.setFileList(listByDtRange); + ffmpegTask.setDuration(duration); + ffmpegTask.setOffsetStart(BigDecimal.valueOf(offset, 3)); + File outFile = new File(faceSample.getDeviceId().toString() + "_" + faceSample.getId() + ".mp4"); + ffmpegTask.setOutputFile(outFile.getAbsolutePath()); + boolean result = startFfmpegTask(ffmpegTask); + if (!result) { + log.warn("视频裁切失败"); + return; + } + log.info("视频裁切成功"); + IStorageAdapter adapter = StorageFactory.use("assets"); + url = adapter.uploadFile(outFile, "video-source", outFile.getName()); + // 上传成功后删除文件 + outFile.delete(); + } else { + url = listByDtRange.get(0).getUrl(); } - log.info("视频裁切成功"); - IStorageAdapter adapter = StorageFactory.use("assets"); - String url = adapter.uploadFile(outFile, "video-source", outFile.getName()); - // 上传成功后删除文件 - outFile.delete(); SourceEntity imgSource = sourceMapper.findBySampleId(faceSample.getId()); SourceEntity sourceEntity = new SourceEntity(); sourceEntity.setId(SnowFlakeUtil.getLongId()); diff --git a/src/main/java/com/ycwl/basic/utils/WxMpUtil.java b/src/main/java/com/ycwl/basic/utils/WxMpUtil.java index 0b0d80c..46dfc20 100644 --- a/src/main/java/com/ycwl/basic/utils/WxMpUtil.java +++ b/src/main/java/com/ycwl/basic/utils/WxMpUtil.java @@ -1,5 +1,6 @@ package com.ycwl.basic.utils; +import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSONObject; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -10,15 +11,35 @@ import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.FileOutputStream; +import java.util.Date; public class WxMpUtil { private static final String GET_WXA_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacode?access_token=%s"; - public static void generateWXAQRCode(String accessToken, String path, String filePath) throws Exception { - String url = String.format(GET_WXA_CODE_URL, accessToken); + private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; + private static String ACCESS_TOKEN = ""; + private static Date expireTime = new Date(); + + private static String getAccessToken(String appId, String appSecret) { + if (ACCESS_TOKEN != null && !ACCESS_TOKEN.isEmpty()) { + if (expireTime.getTime() > System.currentTimeMillis()) { + return ACCESS_TOKEN; + } + } + String url = String.format(ACCESS_TOKEN_URL, appId, appSecret); + String response = HttpUtil.get(url); + JSONObject jsonObject = JSONObject.parseObject(response); + ACCESS_TOKEN = jsonObject.getString("access_token"); + expireTime = new Date(System.currentTimeMillis() + jsonObject.getInteger("expires_in") * 1000); + return ACCESS_TOKEN; + } + + public static void generateWXAQRCode(String appId, String appSecret, String path, String filePath) throws Exception { + String url = String.format(GET_WXA_CODE_URL, getAccessToken(appId, appSecret)); CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost httpPost = new HttpPost(url); JSONObject json = new JSONObject(); + json.put("env_version", "trial"); json.put("path", path); json.put("width", 430); StringEntity entity = new StringEntity(json.toJSONString(), "utf-8"); @@ -36,4 +57,7 @@ public class WxMpUtil { } } + public static void main(String[] args) throws Exception { + generateWXAQRCode("wxe7ff26af70bfc37c", "5252fbbc68513bc77b7cc0052b9f9695", "pages/home/index?scenicId=3942994647776890880", "a.jpg"); + } } diff --git a/src/main/resources/mapper/DeviceMapper.xml b/src/main/resources/mapper/DeviceMapper.xml index 8fa38e2..0998cae 100644 --- a/src/main/resources/mapper/DeviceMapper.xml +++ b/src/main/resources/mapper/DeviceMapper.xml @@ -82,9 +82,10 @@ left join scenic s on d.scenic_id = s.id where d.id = #{id} - + select d.id, d.name, no, d.status, create_at, d.update_at, p.wvp_device_no as device_no, p.wvp_channel_no channel_no from device d + left join device_preview_config p on d.id = p.device_id and p.status = 1 where d.scenic_id = #{scenicId} + \ No newline at end of file diff --git a/src/main/resources/mapper/ScenicMapper.xml b/src/main/resources/mapper/ScenicMapper.xml index 866a7bc..955066a 100644 --- a/src/main/resources/mapper/ScenicMapper.xml +++ b/src/main/resources/mapper/ScenicMapper.xml @@ -99,7 +99,7 @@ delete from scenic_config where scenic_id = #{scenicId} + \ No newline at end of file