diff --git a/src/main/java/com/ycwl/basic/device/DeviceFactory.java b/src/main/java/com/ycwl/basic/device/DeviceFactory.java new file mode 100644 index 0000000..812cec5 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/DeviceFactory.java @@ -0,0 +1,45 @@ +package com.ycwl.basic.device; + +import com.ycwl.basic.device.checker.IDeviceStatusChecker; +import com.ycwl.basic.device.checker.impl.AliOssDeviceChecker; +import com.ycwl.basic.device.checker.impl.AlwaysOnDeviceChecker; +import com.ycwl.basic.device.enums.DeviceStoreTypeEnum; +import com.ycwl.basic.device.operator.IDeviceStorageOperator; +import com.ycwl.basic.device.operator.impl.AliOssStorageOperator; +import com.ycwl.basic.device.operator.impl.LocalStorageOperator; +import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; +import com.ycwl.basic.model.pc.device.entity.DeviceEntity; + +public class DeviceFactory { + public static IDeviceStorageOperator getDeviceStorageOperator(DeviceEntity device, DeviceConfigEntity config) { + IDeviceStorageOperator operator = null; + if (config.getStoreType() == DeviceStoreTypeEnum.ALI_OSS.getType()) { + operator = new AliOssStorageOperator(config.getStoreConfigJson()); + } else if (config.getStoreType() == DeviceStoreTypeEnum.LOCAL.getType()) { + operator = new LocalStorageOperator(config.getStoreConfigJson()); + } + if (operator == null) { + return null; + } + operator.setDevice(device); + operator.setDeviceConfig(config); + return operator; + } + + public static IDeviceStatusChecker getDeviceStatusChecker(DeviceEntity device, DeviceConfigEntity config) { + IDeviceStatusChecker checker = null; + if (config.getOnlineCheck() <= 0) { + checker = new AlwaysOnDeviceChecker(); + } else { + if (config.getStoreType() == DeviceStoreTypeEnum.ALI_OSS.getType()) { + checker = new AliOssDeviceChecker(config.getStoreConfigJson()); + } + } + if (checker == null) { + return null; + } + checker.setDevice(device); + checker.setDeviceConfig(config); + return checker; + } +} diff --git a/src/main/java/com/ycwl/basic/device/IDeviceCommon.java b/src/main/java/com/ycwl/basic/device/IDeviceCommon.java new file mode 100644 index 0000000..58d1bd3 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/IDeviceCommon.java @@ -0,0 +1,9 @@ +package com.ycwl.basic.device; + +import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; +import com.ycwl.basic.model.pc.device.entity.DeviceEntity; + +public interface IDeviceCommon { + void setDevice(DeviceEntity device); + void setDeviceConfig(DeviceConfigEntity config); +} diff --git a/src/main/java/com/ycwl/basic/device/checker/IDeviceStatusChecker.java b/src/main/java/com/ycwl/basic/device/checker/IDeviceStatusChecker.java new file mode 100644 index 0000000..1235bac --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/checker/IDeviceStatusChecker.java @@ -0,0 +1,7 @@ +package com.ycwl.basic.device.checker; + +import com.ycwl.basic.device.IDeviceCommon; + +public interface IDeviceStatusChecker extends IDeviceCommon { + boolean checkDeviceStatus(); +} diff --git a/src/main/java/com/ycwl/basic/device/checker/helper/CommonDeviceChecker.java b/src/main/java/com/ycwl/basic/device/checker/helper/CommonDeviceChecker.java new file mode 100644 index 0000000..b1a5658 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/checker/helper/CommonDeviceChecker.java @@ -0,0 +1,23 @@ +package com.ycwl.basic.device.checker.helper; + +import com.alibaba.fastjson.JSON; +import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; +import com.ycwl.basic.model.pc.device.entity.DeviceEntity; +import lombok.Setter; + +public abstract class CommonDeviceChecker { + protected C config; + @Setter + protected DeviceEntity device; + @Setter + protected DeviceConfigEntity deviceConfig; + + public CommonDeviceChecker(String configJson) { + config = JSON.parseObject(configJson, getConfigClass()); + } + + @SuppressWarnings("unchecked") + private Class getConfigClass() { + return (Class) ((java.lang.reflect.ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + } +} diff --git a/src/main/java/com/ycwl/basic/device/checker/impl/AliOssDeviceChecker.java b/src/main/java/com/ycwl/basic/device/checker/impl/AliOssDeviceChecker.java new file mode 100644 index 0000000..a80c349 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/checker/impl/AliOssDeviceChecker.java @@ -0,0 +1,88 @@ +package com.ycwl.basic.device.checker.impl; + +import cn.hutool.core.date.DateUtil; +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.OSSException; +import com.aliyun.oss.model.ListObjectsV2Request; +import com.aliyun.oss.model.ListObjectsV2Result; +import com.aliyun.oss.model.OSSObjectSummary; +import com.ycwl.basic.device.checker.IDeviceStatusChecker; +import com.ycwl.basic.device.checker.helper.CommonDeviceChecker; +import com.ycwl.basic.device.entity.alioss.DeviceAliOssConfig; +import com.ycwl.basic.device.entity.common.FileObject; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +public class AliOssDeviceChecker extends CommonDeviceChecker implements IDeviceStatusChecker { + public AliOssDeviceChecker(String configJson) { + super(configJson); + } + + private OSS getClient() { + return new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret()); + } + + private List getOssFileListByPrefix(String prefix) { + OSS ossClient = getClient(); + ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(config.getBucketName()); + listObjectsV2Request.setPrefix(config.getPrefix() + prefix); + boolean isTruncated = true; + String continuationToken = null; + List objectList = new ArrayList<>(); + try { + while (isTruncated) { + if (continuationToken != null) { + listObjectsV2Request.setContinuationToken(continuationToken); + } + + // 列举文件。 + ListObjectsV2Result result = ossClient.listObjectsV2(listObjectsV2Request); + + objectList.addAll(result.getObjectSummaries()); + + isTruncated = result.isTruncated(); + continuationToken = result.getNextContinuationToken(); + } + return objectList.stream().filter(item -> item.getKey().endsWith(".ts")).map(item -> { + FileObject object = new FileObject(); + object.setPath(item.getKey()); + object.setNeedDownload(true); + String[] splitDir = item.getKey().split("/"); + String splitDate = splitDir[splitDir.length - 2]; + String splitName = splitDir[splitDir.length - 1]; + String[] splitExt = splitName.split("\\.", 2); + String[] splitDt = splitExt[0].split("_", 2); + String createTime = splitDt[0]; + String endTime = splitDt[1]; + object.setName(splitName); + object.setEndTime(DateUtil.parse(splitDate+endTime, "yyyyMMddHHmmss")); + object.setCreateTime(DateUtil.parse(splitDate+createTime, "yyyyMMddHHmmss")); + return object; + }).collect(Collectors.toList()); + } catch (OSSException e) { + log.error("获取OSS文件列表失败", e); + } + return null; + } + @Override + public boolean checkDeviceStatus() { + Date now = new Date(); + if (deviceConfig.getOnlineMaxInterval() == null) { + // 没有合适配置 + return true; + } + Date startTime = DateUtil.offsetMinute(now, -deviceConfig.getOnlineMaxInterval()); + List fileList = getOssFileListByPrefix(DateUtil.format(startTime, "yyyyMMdd/HHmm")); + if (fileList != null) { + return !fileList.isEmpty(); + } else { + return false; + } + } +} diff --git a/src/main/java/com/ycwl/basic/device/checker/impl/AlwaysOnDeviceChecker.java b/src/main/java/com/ycwl/basic/device/checker/impl/AlwaysOnDeviceChecker.java new file mode 100644 index 0000000..05ed4b2 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/checker/impl/AlwaysOnDeviceChecker.java @@ -0,0 +1,20 @@ +package com.ycwl.basic.device.checker.impl; + +import com.ycwl.basic.device.checker.IDeviceStatusChecker; +import com.ycwl.basic.device.checker.helper.CommonDeviceChecker; +import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; +import com.ycwl.basic.model.pc.device.entity.DeviceEntity; +import lombok.Setter; + +@SuppressWarnings("LombokSetterMayBeUsed") +public class AlwaysOnDeviceChecker implements IDeviceStatusChecker { + @Setter + private DeviceEntity device; + @Setter + private DeviceConfigEntity deviceConfig; + + @Override + public boolean checkDeviceStatus() { + return true; + } +} diff --git a/src/main/java/com/ycwl/basic/device/entity/alioss/DeviceAliOssConfig.java b/src/main/java/com/ycwl/basic/device/entity/alioss/DeviceAliOssConfig.java new file mode 100644 index 0000000..0ae272a --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/entity/alioss/DeviceAliOssConfig.java @@ -0,0 +1,14 @@ +package com.ycwl.basic.device.entity.alioss; + +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +@Data +public class DeviceAliOssConfig { + private String accessKeyId; + private String accessKeySecret; + private String bucketName; + private String endpoint; + private String region; + private String prefix; +} diff --git a/src/main/java/com/ycwl/basic/device/entity/common/FileObject.java b/src/main/java/com/ycwl/basic/device/entity/common/FileObject.java new file mode 100644 index 0000000..27d51bd --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/entity/common/FileObject.java @@ -0,0 +1,15 @@ +package com.ycwl.basic.device.entity.common; + +import lombok.Data; + +import java.util.Date; + +@Data +public class FileObject { + private String path; + private String name; + private String url; + private boolean needDownload = false; + private Date createTime; + private Date endTime; +} diff --git a/src/main/java/com/ycwl/basic/device/entity/local/DeviceLocalConfig.java b/src/main/java/com/ycwl/basic/device/entity/local/DeviceLocalConfig.java new file mode 100644 index 0000000..6293e1b --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/entity/local/DeviceLocalConfig.java @@ -0,0 +1,7 @@ +package com.ycwl.basic.device.entity.local; + +import lombok.Data; + +@Data +public class DeviceLocalConfig { +} diff --git a/src/main/java/com/ycwl/basic/device/enums/DeviceStoreTypeEnum.java b/src/main/java/com/ycwl/basic/device/enums/DeviceStoreTypeEnum.java new file mode 100644 index 0000000..5d24254 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/enums/DeviceStoreTypeEnum.java @@ -0,0 +1,23 @@ +package com.ycwl.basic.device.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum DeviceStoreTypeEnum { + ALI_OSS(1, "阿里云OSS"), + LOCAL(2, "本地文件"); + + private final int type; + private final String remark; + + public static DeviceStoreTypeEnum getByType(int type) { + for (DeviceStoreTypeEnum e : values()) { + if (e.type == type) { + return e; + } + } + return null; + } +} diff --git a/src/main/java/com/ycwl/basic/device/operator/IDeviceStorageOperator.java b/src/main/java/com/ycwl/basic/device/operator/IDeviceStorageOperator.java new file mode 100644 index 0000000..e8b28cd --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/operator/IDeviceStorageOperator.java @@ -0,0 +1,26 @@ +package com.ycwl.basic.device.operator; + +import com.ycwl.basic.device.IDeviceCommon; +import com.ycwl.basic.device.entity.common.FileObject; + +import java.util.Date; +import java.util.List; + +public interface IDeviceStorageOperator extends IDeviceCommon { + /** + * 获取指定时间范围内的文件列表 + * + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return + */ + List getFileListByDtRange(Date startDate, Date endDate); + + /** + * 删除指定日期之前的文件,不包含指定的日期当天 + * + * @param date 指定日期,不包含指定日期当天 + * @return + */ + boolean removeFilesBeforeDate(Date date); +} diff --git a/src/main/java/com/ycwl/basic/device/operator/helper/CommonPieceGetter.java b/src/main/java/com/ycwl/basic/device/operator/helper/CommonPieceGetter.java new file mode 100644 index 0000000..9c73030 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/operator/helper/CommonPieceGetter.java @@ -0,0 +1,24 @@ +package com.ycwl.basic.device.operator.helper; + +import com.alibaba.fastjson.JSON; +import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; +import com.ycwl.basic.model.pc.device.entity.DeviceEntity; +import lombok.Setter; + +public abstract class CommonPieceGetter { + protected C config; + @Setter + private DeviceEntity device; + @Setter + private DeviceConfigEntity deviceConfig; + + public CommonPieceGetter(String configJson) { + config = JSON.parseObject(configJson, getConfigClass()); + } + + @SuppressWarnings("unchecked") + private Class getConfigClass() { + return (Class) ((java.lang.reflect.ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + } + +} diff --git a/src/main/java/com/ycwl/basic/device/operator/impl/AliOssStorageOperator.java b/src/main/java/com/ycwl/basic/device/operator/impl/AliOssStorageOperator.java new file mode 100644 index 0000000..5f0e5f5 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/operator/impl/AliOssStorageOperator.java @@ -0,0 +1,151 @@ +package com.ycwl.basic.device.operator.impl; + +import cn.hutool.core.date.DateUtil; +import com.aliyun.oss.HttpMethod; +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.OSSException; +import com.aliyun.oss.model.DeleteObjectsRequest; +import com.aliyun.oss.model.ListObjectsV2Request; +import com.aliyun.oss.model.ListObjectsV2Result; +import com.aliyun.oss.model.OSSObjectSummary; +import com.ycwl.basic.device.entity.alioss.DeviceAliOssConfig; +import com.ycwl.basic.device.entity.common.FileObject; +import com.ycwl.basic.device.operator.IDeviceStorageOperator; +import com.ycwl.basic.device.operator.helper.CommonPieceGetter; +import lombok.extern.slf4j.Slf4j; + +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +public class AliOssStorageOperator extends CommonPieceGetter implements IDeviceStorageOperator { + public AliOssStorageOperator(String configJson) { + super(configJson); + } + + private OSS getClient() { + return new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret()); + } + + private List getOssObjectListByPrefix(String prefix) { + OSS ossClient = getClient(); + ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(config.getBucketName()); + listObjectsV2Request.setPrefix(config.getPrefix() + prefix); + boolean isTruncated = true; + String continuationToken = null; + List objectList = new ArrayList<>(); + try { + while (isTruncated) { + if (continuationToken != null) { + listObjectsV2Request.setContinuationToken(continuationToken); + } + + // 列举文件。 + ListObjectsV2Result result = ossClient.listObjectsV2(listObjectsV2Request); + + objectList.addAll(result.getObjectSummaries()); + + isTruncated = result.isTruncated(); + continuationToken = result.getNextContinuationToken(); + } + return objectList; + } catch (OSSException e) { + log.error("获取OSS文件列表失败", e); + } + return null; + } + + private List getOssFileListByPrefix(String prefix) { + OSS ossClient = getClient(); + List objectList = getOssObjectListByPrefix(prefix); + if (objectList == null) { + return null; + } + return objectList.stream().filter(item -> item.getKey().endsWith(".ts")).map(item -> { + FileObject object = new FileObject(); + object.setPath(item.getKey()); + URL url = ossClient.generatePresignedUrl(item.getBucketName(), item.getKey(), DateUtil.offsetHour(new Date(), 8), HttpMethod.GET); + object.setUrl(url.toString()); + object.setNeedDownload(true); + String[] splitDir = item.getKey().split("/"); + String splitDate = splitDir[splitDir.length - 2]; + String splitName = splitDir[splitDir.length - 1]; + String[] splitExt = splitName.split("\\.", 2); + String[] splitDt = splitExt[0].split("_", 2); + String createTime = splitDt[0]; + String endTime = splitDt[1]; + object.setName(splitName); + object.setEndTime(DateUtil.parse(splitDate+endTime, "yyyyMMddHHmmss")); + object.setCreateTime(DateUtil.parse(splitDate+createTime, "yyyyMMddHHmmss")); + return object; + }).collect(Collectors.toList()); + } + + private boolean removeFilesByPrefix(String prefix) { + OSS ossClient = getClient(); + List objectList = getOssObjectListByPrefix(prefix); + if (objectList == null) { + return false; + } + DeleteObjectsRequest request = new DeleteObjectsRequest(config.getBucketName()); + request.setKeys(objectList.stream().map(OSSObjectSummary::getKey).collect(Collectors.toList())); + try { + ossClient.deleteObjects(request); + return true; + } catch (OSSException e) { + return false; + } finally { + ossClient.shutdown(); + } + } + + @Override + public List getFileListByDtRange(Date startDate, Date endDate) { + if (startDate == null || endDate == null) { + return null; + } + List fileList = new ArrayList<>(); + if (startDate.after(endDate)) { + return fileList; + } + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startDate); + calendar.set(Calendar.SECOND, 0); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd/HHmm"); + while (calendar.getTime().before(endDate)) { + String prefix = dateFormat.format(calendar.getTime()); + List fileListByPrefix = getOssFileListByPrefix(prefix); + if (fileListByPrefix == null) { + return null; + } + fileList.addAll(fileListByPrefix); + calendar.add(Calendar.MINUTE, 1); + } + calendar.clear(); + return fileList.stream() + .sorted(Comparator.comparing(FileObject::getCreateTime)) + .filter(item -> item.getCreateTime().after(startDate)) + .filter(item -> item.getCreateTime().before(endDate)) + .collect(Collectors.toList()); + } + + @Override + public boolean removeFilesBeforeDate(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.HOUR_OF_DAY, 0); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd/"); + calendar.add(Calendar.DAY_OF_MONTH, -1); + String prefix = dateFormat.format(calendar.getTime()); + return removeFilesByPrefix(prefix); + } +} diff --git a/src/main/java/com/ycwl/basic/device/operator/impl/LocalStorageOperator.java b/src/main/java/com/ycwl/basic/device/operator/impl/LocalStorageOperator.java new file mode 100644 index 0000000..d321f85 --- /dev/null +++ b/src/main/java/com/ycwl/basic/device/operator/impl/LocalStorageOperator.java @@ -0,0 +1,26 @@ +package com.ycwl.basic.device.operator.impl; + +import com.ycwl.basic.device.entity.common.FileObject; +import com.ycwl.basic.device.entity.local.DeviceLocalConfig; +import com.ycwl.basic.device.operator.IDeviceStorageOperator; +import com.ycwl.basic.device.operator.helper.CommonPieceGetter; + +import java.util.Collections; +import java.util.Date; +import java.util.List; + +public class LocalStorageOperator extends CommonPieceGetter implements IDeviceStorageOperator { + public LocalStorageOperator(String configJson) { + super(configJson); + } + + @Override + public List getFileListByDtRange(Date startDate, Date endDate) { + return Collections.emptyList(); + } + + @Override + public boolean removeFilesBeforeDate(Date date) { + return false; + } +} diff --git a/src/main/java/com/ycwl/basic/mapper/pc/DeviceMapper.java b/src/main/java/com/ycwl/basic/mapper/pc/DeviceMapper.java index 86f81df..bb983cb 100644 --- a/src/main/java/com/ycwl/basic/mapper/pc/DeviceMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/pc/DeviceMapper.java @@ -1,6 +1,7 @@ package com.ycwl.basic.mapper.pc; import com.ycwl.basic.model.mobile.scenic.ScenicDeviceCountVO; +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.DeviceAddOrUpdateReq; import com.ycwl.basic.model.pc.device.req.DeviceReqQuery; @@ -24,7 +25,10 @@ public interface DeviceMapper { int update(DeviceAddOrUpdateReq deviceReqQuery); int updateStatus(Long id); + DeviceEntity getByDeviceId(Long deviceId); List listByScenicId(Long scenicId); ScenicDeviceCountVO deviceCountByScenicId(@Param("scenicId") Long scenicId,@Param("userId") Long userId); + + DeviceConfigEntity getConfigByDeviceId(Long deviceId); } diff --git a/src/main/java/com/ycwl/basic/mapper/pc/FaceMapper.java b/src/main/java/com/ycwl/basic/mapper/pc/FaceMapper.java index d9e95cc..7ebc1ac 100644 --- a/src/main/java/com/ycwl/basic/mapper/pc/FaceMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/pc/FaceMapper.java @@ -16,6 +16,7 @@ import java.util.List; @Mapper public interface FaceMapper { List list(FaceReqQuery faceReqQuery); + List listByScenicIdAndNotFinished(Long scenicId); FaceRespVO getById(Long id); int add(FaceEntity face); int deleteById(Long id); @@ -23,4 +24,6 @@ public interface FaceMapper { int update(FaceEntity face); FaceRespVO getByMemberId(Long userId); + + int finishedJourney(Long faceId); } diff --git a/src/main/java/com/ycwl/basic/mapper/pc/FaceSampleMapper.java b/src/main/java/com/ycwl/basic/mapper/pc/FaceSampleMapper.java index a282bf0..cf41d0d 100644 --- a/src/main/java/com/ycwl/basic/mapper/pc/FaceSampleMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/pc/FaceSampleMapper.java @@ -21,4 +21,6 @@ public interface FaceSampleMapper { int deleteById(Long id); int deleteByIds(@Param("list") List ids); int update(FaceSampleEntity faceSample); + + List listByIds(List list); } diff --git a/src/main/java/com/ycwl/basic/mapper/pc/ScenicMapper.java b/src/main/java/com/ycwl/basic/mapper/pc/ScenicMapper.java index f80389f..f417196 100644 --- a/src/main/java/com/ycwl/basic/mapper/pc/ScenicMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/pc/ScenicMapper.java @@ -30,6 +30,7 @@ public interface ScenicMapper { int updateStatus(Long id); + ScenicConfigEntity getConfig(Long scenicId); /** * 添加景区配置 * @@ -51,7 +52,7 @@ public interface ScenicMapper { * * @param scenicId */ - void deleteConfigByscenicId(Long scenicId); + void deleteConfigByScenicId(Long scenicId); List appList(ScenicReqQuery scenicReqQuery); diff --git a/src/main/java/com/ycwl/basic/mapper/pc/TemplateMapper.java b/src/main/java/com/ycwl/basic/mapper/pc/TemplateMapper.java index 1305535..2f76cdb 100644 --- a/src/main/java/com/ycwl/basic/mapper/pc/TemplateMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/pc/TemplateMapper.java @@ -3,6 +3,7 @@ package com.ycwl.basic.mapper.pc; import com.ycwl.basic.model.pc.task.entity.TaskEntity; 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.entity.TemplateConfigEntity; import com.ycwl.basic.model.pc.template.entity.TemplateEntity; import com.ycwl.basic.model.pc.template.req.TemplateReqQuery; import com.ycwl.basic.model.pc.template.resp.TemplateRespVO; @@ -26,4 +27,9 @@ public interface TemplateMapper { int deleteByPid(Long pid); int deleteByScenicId(Long scenicId); List getByPid(Long id); + TemplateConfigEntity getConfig(Long templateId); + int addConfig(TemplateConfigEntity templateConfig); + int updateConfigById(TemplateConfigEntity templateConfigEntity); + int deleteConfigByTemplateId(Long templateId); + int deleteConfigById(Long id); } diff --git a/src/main/java/com/ycwl/basic/mapper/pc/VideoMapper.java b/src/main/java/com/ycwl/basic/mapper/pc/VideoMapper.java index e6ab007..124afa1 100644 --- a/src/main/java/com/ycwl/basic/mapper/pc/VideoMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/pc/VideoMapper.java @@ -6,6 +6,7 @@ import com.ycwl.basic.model.pc.template.resp.TemplateRespVO; import com.ycwl.basic.model.pc.video.entity.VideoEntity; import com.ycwl.basic.model.pc.video.req.VideoReqQuery; import com.ycwl.basic.model.pc.video.resp.VideoRespVO; +import lombok.NonNull; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -22,4 +23,6 @@ public interface VideoMapper { int add(VideoEntity task); int deleteById(Long id); int update(VideoEntity task); + + VideoEntity findByTaskId(@NonNull Long taskId); } 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 new file mode 100644 index 0000000..e37d35e --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/pc/device/entity/DeviceConfigEntity.java @@ -0,0 +1,59 @@ +package com.ycwl.basic.model.pc.device.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@TableName("device_config") +public class DeviceConfigEntity { + private Long id; + /** + * 设备id + */ + private Long deviceId; + /** + * 启用时间 + */ + private Date startTime; + /** + * 结束时间 + */ + private Date endTime; + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + /** + * 存储类型 + */ + private Integer storeType; + /** + * 存储配置 + */ + private String storeConfigJson; + /** + * 存储过期天数 + */ + private Integer storeExpireDay; + /** + * 检测设备是否在线 + */ + private Integer onlineCheck; + /** + * 检测设备是否在线最大间隔 + */ + private Integer onlineMaxInterval; + /** + * 切割时,取人脸前多少秒的视频 + */ + private BigDecimal cutPre; + /** + * 切割时,取人脸后多少秒的视频 + */ + private BigDecimal cutPost; +} diff --git a/src/main/java/com/ycwl/basic/model/pc/face/entity/FaceEntity.java b/src/main/java/com/ycwl/basic/model/pc/face/entity/FaceEntity.java index 9db8b04..8c74f5a 100644 --- a/src/main/java/com/ycwl/basic/model/pc/face/entity/FaceEntity.java +++ b/src/main/java/com/ycwl/basic/model/pc/face/entity/FaceEntity.java @@ -16,6 +16,7 @@ import java.util.Date; public class FaceEntity { @TableId private Long id; + private Long scenicId; /** * 人脸得分 */ diff --git a/src/main/java/com/ycwl/basic/model/pc/face/req/FaceReqQuery.java b/src/main/java/com/ycwl/basic/model/pc/face/req/FaceReqQuery.java index e6014e4..cba2db3 100644 --- a/src/main/java/com/ycwl/basic/model/pc/face/req/FaceReqQuery.java +++ b/src/main/java/com/ycwl/basic/model/pc/face/req/FaceReqQuery.java @@ -16,6 +16,8 @@ import java.util.Date; @Data @ApiModel("人脸查询参数") public class FaceReqQuery extends BaseQueryParameterReq { + @ApiModelProperty("景区id") + private Long scenicId; @ApiModelProperty("会员id") private Long memberId; @ApiModelProperty("用户上传的人脸照片") diff --git a/src/main/java/com/ycwl/basic/model/pc/order/req/OrderReqQuery.java b/src/main/java/com/ycwl/basic/model/pc/order/req/OrderReqQuery.java index 55940f4..41c2b53 100644 --- a/src/main/java/com/ycwl/basic/model/pc/order/req/OrderReqQuery.java +++ b/src/main/java/com/ycwl/basic/model/pc/order/req/OrderReqQuery.java @@ -18,6 +18,7 @@ import java.util.Date; @ApiModel(value = "订单查询对象") public class OrderReqQuery extends BaseQueryParameterReq { private Long id; + private Long scenicId; private Long memberId; @ApiModelProperty("用户昵称") private String memberNickname; diff --git a/src/main/java/com/ycwl/basic/model/pc/order/resp/OrderRespVO.java b/src/main/java/com/ycwl/basic/model/pc/order/resp/OrderRespVO.java index b61d74d..bf6b9b9 100644 --- a/src/main/java/com/ycwl/basic/model/pc/order/resp/OrderRespVO.java +++ b/src/main/java/com/ycwl/basic/model/pc/order/resp/OrderRespVO.java @@ -102,4 +102,6 @@ public class OrderRespVO { private Date refundAt; @ApiModelProperty("订单明细") private List orderItemList; + private Long scenicId; + private String scenicName; } 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 da1e9c6..2e04211 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 @@ -38,4 +38,21 @@ public class ScenicConfigEntity { */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; + + /** + * 预约流程,1-预约,2-在线,3-全部 + */ + private Integer bookRoutine; + /** + * 样本保存时间 + */ + private Integer sampleStoreDay; + /** + * 视频保存时间 + */ + private Integer videoStoreDay; + /** + * 最大行程时长 + */ + private Integer maxJourneyHour; } diff --git a/src/main/java/com/ycwl/basic/model/pc/scenic/req/ScenicAddOrUpdateReq.java b/src/main/java/com/ycwl/basic/model/pc/scenic/req/ScenicAddOrUpdateReq.java index 1cfe0c9..2ff5333 100644 --- a/src/main/java/com/ycwl/basic/model/pc/scenic/req/ScenicAddOrUpdateReq.java +++ b/src/main/java/com/ycwl/basic/model/pc/scenic/req/ScenicAddOrUpdateReq.java @@ -1,7 +1,6 @@ package com.ycwl.basic.model.pc.scenic.req; import com.fasterxml.jackson.annotation.JsonFormat; -import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -78,8 +77,6 @@ public class ScenicAddOrUpdateReq { private Date createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; - @ApiModelProperty("景区配置") - private ScenicConfigEntity scenicConfig; @ApiModelProperty("景区源素材价格,元") private BigDecimal price; diff --git a/src/main/java/com/ycwl/basic/model/pc/scenic/resp/ScenicRespVO.java b/src/main/java/com/ycwl/basic/model/pc/scenic/resp/ScenicRespVO.java index 6c5eb34..47f1008 100644 --- a/src/main/java/com/ycwl/basic/model/pc/scenic/resp/ScenicRespVO.java +++ b/src/main/java/com/ycwl/basic/model/pc/scenic/resp/ScenicRespVO.java @@ -79,13 +79,11 @@ public class ScenicRespVO { * 状态 1启用0关闭 */ @ApiModelProperty("状态 1启用0关闭") - private String status; + private Integer status; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; - @ApiModelProperty("景区配置") - private ScenicConfigEntity scenicConfig; @ApiModelProperty("景区源素材价格,元") private BigDecimal price; @ApiModelProperty("镜头数") diff --git a/src/main/java/com/ycwl/basic/model/pc/source/entity/SourceEntity.java b/src/main/java/com/ycwl/basic/model/pc/source/entity/SourceEntity.java index 97bf763..e3c9371 100644 --- a/src/main/java/com/ycwl/basic/model/pc/source/entity/SourceEntity.java +++ b/src/main/java/com/ycwl/basic/model/pc/source/entity/SourceEntity.java @@ -30,6 +30,10 @@ public class SourceEntity { * 所属用户 */ private Long memberId; + /** + * 人脸样本id + */ + private Long faceSampleId; /** * 原素材类型:1视频,2图像 */ diff --git a/src/main/java/com/ycwl/basic/model/pc/template/entity/TemplateConfigEntity.java b/src/main/java/com/ycwl/basic/model/pc/template/entity/TemplateConfigEntity.java new file mode 100644 index 0000000..02d81ff --- /dev/null +++ b/src/main/java/com/ycwl/basic/model/pc/template/entity/TemplateConfigEntity.java @@ -0,0 +1,16 @@ +package com.ycwl.basic.model.pc.template.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +@Data +@TableName("template_config") +public class TemplateConfigEntity { + private Long id; + private Long templateId; + private Integer isDefault; + private Date createDate; + private Integer minimalPlaceholderFill; +} diff --git a/src/main/java/com/ycwl/basic/service/impl/pc/MenuServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/pc/MenuServiceImpl.java index 2a32b77..f383a33 100644 --- a/src/main/java/com/ycwl/basic/service/impl/pc/MenuServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/pc/MenuServiceImpl.java @@ -7,6 +7,7 @@ import com.ycwl.basic.service.pc.MenuService; import com.ycwl.basic.utils.ApiResponse; import com.ycwl.basic.utils.SnowFlakeUtil; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @@ -15,7 +16,7 @@ import java.util.List; * @Author:longbinbin * @Date:2024/12/3 10:16 */ - +@Service public class MenuServiceImpl implements MenuService { @Autowired diff --git a/src/main/java/com/ycwl/basic/service/impl/pc/ScenicServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/pc/ScenicServiceImpl.java index e67db49..4a2bfce 100644 --- a/src/main/java/com/ycwl/basic/service/impl/pc/ScenicServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/pc/ScenicServiceImpl.java @@ -96,7 +96,7 @@ public class ScenicServiceImpl implements ScenicService { public ApiResponse deleteById(Long id) { int i = scenicMapper.deleteById(id); if (i > 0) { - scenicMapper.deleteConfigByscenicId(id); + scenicMapper.deleteConfigByScenicId(id); scenicAccountMapper.deleteByScenicId(id); return ApiResponse.success(true); }else { diff --git a/src/main/java/com/ycwl/basic/service/impl/task/TaskFaceServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/task/TaskFaceServiceImpl.java index a0e0476..b38418b 100644 --- a/src/main/java/com/ycwl/basic/service/impl/task/TaskFaceServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/task/TaskFaceServiceImpl.java @@ -21,6 +21,7 @@ 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.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.task.resp.AddFaceRespVo; import com.ycwl.basic.model.task.resp.SearchFaceRespVo; import com.ycwl.basic.service.task.TaskFaceService; @@ -84,13 +85,12 @@ public class TaskFaceServiceImpl implements TaskFaceService { .map(SearchFaceResponse.Data.MatchListItem.FaceItemsItem::getExtraData) .map(Long::parseLong) .collect(Collectors.toList()); - faceEntity.setMatchSampleIds(StringUtils.joinWith(",", faceSampleIds)); + faceEntity.setMatchSampleIds(StringUtils.join(faceSampleIds, ",")); faceMapper.update(faceEntity); respVo.setSampleListIds(faceSampleIds); respVo.setScore(matchList.get(0).getQualitieScore()); return respVo; } catch (Exception e) { - e.printStackTrace(); log.error("人脸搜索失败:{}", e.getMessage()); throw new BaseException(e.getMessage()); } @@ -136,7 +136,15 @@ public class TaskFaceServiceImpl implements TaskFaceService { FaceSampleReqQuery query = new FaceSampleReqQuery(); query.setDeviceId(scenicId); faceSampleMapper.list(query); - Date thatDay = DateUtil.offsetDay(new Date(), -3); + ScenicConfigEntity scenicConfig = scenicMapper.getConfig(scenicId); + if (scenicConfig == null) { + return; + } + Integer sampleStoreDay = scenicConfig.getSampleStoreDay(); + if (sampleStoreDay == null) { + sampleStoreDay = 3; + } + Date thatDay = DateUtil.offsetDay(new Date(), -sampleStoreDay); Date dayStart = DateUtil.beginOfDay(thatDay); Date dayEnd = DateUtil.endOfDay(thatDay); query.setStartTime(dayStart); diff --git a/src/main/java/com/ycwl/basic/service/impl/task/TaskTaskServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/task/TaskTaskServiceImpl.java index f72d384..c9fdbe3 100644 --- a/src/main/java/com/ycwl/basic/service/impl/task/TaskTaskServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/task/TaskTaskServiceImpl.java @@ -8,6 +8,7 @@ import com.ycwl.basic.mapper.pc.RenderWorkerMapper; import com.ycwl.basic.mapper.pc.SourceMapper; import com.ycwl.basic.mapper.pc.TaskMapper; import com.ycwl.basic.mapper.pc.TemplateMapper; +import com.ycwl.basic.mapper.pc.VideoMapper; import com.ycwl.basic.model.pc.face.resp.FaceRespVO; import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO; import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity; @@ -16,6 +17,7 @@ import com.ycwl.basic.model.pc.task.entity.TaskEntity; import com.ycwl.basic.model.pc.task.resp.TaskRespVO; import com.ycwl.basic.model.pc.template.req.TemplateReqQuery; import com.ycwl.basic.model.pc.template.resp.TemplateRespVO; +import com.ycwl.basic.model.pc.video.entity.VideoEntity; import com.ycwl.basic.model.task.req.ClientStatusReqVo; import com.ycwl.basic.model.task.req.TaskReqVo; import com.ycwl.basic.model.task.req.WorkerAuthReqVo; @@ -57,6 +59,8 @@ public class TaskTaskServiceImpl implements TaskService { private SourceMapper sourceMapper; @Autowired private OssUtil ossUtil; + @Autowired + private VideoMapper videoMapper; private RenderWorkerEntity getWorker(@NonNull WorkerAuthReqVo req) { String accessKey = req.getAccessKey(); @@ -112,9 +116,7 @@ public class TaskTaskServiceImpl implements TaskService { } List taskList = taskMapper.selectNotRunning(); resp.setTasks(taskList); - taskList.forEach(task -> { - taskMapper.assignToWorker(task.getId(), worker.getId()); - }); + taskList.forEach(task -> taskMapper.assignToWorker(task.getId(), worker.getId())); // return Task return resp; } @@ -131,9 +133,7 @@ public class TaskTaskServiceImpl implements TaskService { } Map> sourcesMap = Arrays.stream(faceRespVO.getMatchSampleIds().split(",")) .map(Long::valueOf) - .map(sampleId -> { - return faceSampleMapper.getById(sampleId); - }) + .map(sampleId -> faceSampleMapper.getById(sampleId)) .filter(Objects::nonNull) .map(FaceSampleRespVO::getSourceId) .map(sourceId -> sourceMapper.getById(sourceId)) @@ -178,6 +178,22 @@ public class TaskTaskServiceImpl implements TaskService { taskUpdate.setStatus(1); taskUpdate.setWorkerId(worker.getId()); taskMapper.update(taskUpdate); + VideoEntity video = videoMapper.findByTaskId(taskId); + if (video != null) { + video.setVideoUrl(task.getVideoUrl()); + videoMapper.update(video); + } else { + video = new VideoEntity(); + video.setId(SnowFlakeUtil.getLongId()); + video.setScenicId(task.getScenicId()); + video.setMemberId(task.getMemberId()); + video.setTemplateId(task.getTemplateId()); + video.setTaskId(taskId); + video.setWorkerId(worker.getId()); + video.setVideoUrl(task.getVideoUrl()); + video.setCreateTime(new Date()); + videoMapper.add(video); + } } @Override diff --git a/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java b/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java new file mode 100644 index 0000000..41932b5 --- /dev/null +++ b/src/main/java/com/ycwl/basic/task/DynamicTaskGenerator.java @@ -0,0 +1,142 @@ +package com.ycwl.basic.task; + +import com.ycwl.basic.mapper.pc.DeviceMapper; +import com.ycwl.basic.mapper.pc.FaceMapper; +import com.ycwl.basic.mapper.pc.FaceSampleMapper; +import com.ycwl.basic.mapper.pc.ScenicMapper; +import com.ycwl.basic.mapper.pc.TemplateMapper; +import com.ycwl.basic.model.pc.face.resp.FaceRespVO; +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.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.service.task.TaskFaceService; +import com.ycwl.basic.service.task.TaskService; +import lombok.extern.slf4j.Slf4j; +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.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +@Component +@EnableScheduling +@Slf4j +public class DynamicTaskGenerator { + @Autowired + private ScenicMapper scenicMapper; + @Autowired + private TemplateMapper templateMapper; + @Autowired + private FaceMapper faceMapper; + @Autowired + private TaskFaceService faceService; + @Autowired + private FaceSampleMapper faceSampleMapper; + @Autowired + private TaskService taskService; + @Autowired + private DeviceMapper deviceMapper; + + @Scheduled(cron = "0 0 * * * ?") + public void dynamicTask() { + List scenicList = scenicMapper.list(new ScenicReqQuery()); + for (ScenicRespVO scenic : scenicList) { + log.info("定时任务执行,当前景区:{}", scenic.getName()); + ScenicConfigEntity scenicConfig = scenicMapper.getConfig(scenic.getId()); + if (scenicConfig == null || scenicConfig.getBookRoutine() == 2) { + log.info("当前景区{},未启用提前预约流程", scenic.getName()); + continue; + } + log.info("当前景区{},启用了提前预约流程", scenic.getName()); + TemplateReqQuery templateQuery = new TemplateReqQuery(); + templateQuery.setScenicId(scenic.getId()); + List templateList = templateMapper.list(templateQuery); + for (TemplateRespVO template : templateList) { + log.info("当前景区{},启用了提前预约流程,模板:{}", scenic.getName(), template.getName()); + if (template.getStatus() == 0) { + log.info("当前模板{}未启用,跳过", template.getName()); + continue; + } + TemplateConfigEntity templateConfig = templateMapper.getConfig(template.getId()); + if (templateConfig == null) { + log.info("当前模板{}未配置,跳过", template.getName()); + continue; + } + if (templateConfig.getIsDefault() == 0) { + if (scenicConfig.getBookRoutine() == 1) { + log.info("当前模板{}未启用默认,且景区启用预约流程,跳过", template.getName()); + continue; + } + } + Integer minimalPlaceholderFill = templateConfig.getMinimalPlaceholderFill(); + List placeholderList = new ArrayList<>(); + if (minimalPlaceholderFill == null) { + minimalPlaceholderFill = 0; + } + List subTemplateList = templateMapper.getByPid(template.getId()); + for (TemplateRespVO subTemplate : subTemplateList) { + if (subTemplate.getIsPlaceholder() == 1) { + placeholderList.add(subTemplate.getSourceUrl()); + } + } + if (minimalPlaceholderFill == 0) { + for (TemplateRespVO subTemplate : subTemplateList) { + if (subTemplate.getIsPlaceholder() == 1) { + minimalPlaceholderFill += 1; + } + } + } + log.info("当前模板{}启用默认,最小占位素材:{}", template.getName(), minimalPlaceholderFill); + // 查找人脸样本 + List list = faceMapper.listByScenicIdAndNotFinished(scenic.getId()); + for (FaceRespVO face : list) { + log.info("当前模板{}启用默认,人脸样本:{}", template.getName(), face.getFaceUrl()); + if (((new Date()).getTime() - face.getCreateAt().getTime()) > scenicConfig.getMaxJourneyHour() * 3600 * 1000) { + log.info("当前人脸样本{}已超过最大游玩{}小时,自动检测人脸", face.getFaceUrl(), scenicConfig.getMaxJourneyHour()); + List oldMatchedSampleListIds = new ArrayList<>(); + if (face.getMatchSampleIds() != null) { + oldMatchedSampleListIds = Arrays.asList(face.getMatchSampleIds().split(",")); + } + SearchFaceRespVo searchFace = faceService.searchFace(scenic.getId(), face.getId()); + if (oldMatchedSampleListIds.size() == searchFace.getSampleListIds().size()) { + boolean isEqual = true; + for (Long sampleId : searchFace.getSampleListIds()) { + if (!oldMatchedSampleListIds.contains(sampleId.toString())) { + isEqual = false; + break; + } + } + if (isEqual) { + log.info("当前人脸样本{}已超过最大游玩{}小时,但人脸检测结果与上次相同,跳过", face.getFaceUrl(), scenicConfig.getMaxJourneyHour()); + continue; + } + } + List faceSampleList = faceSampleMapper.listByIds(searchFace.getSampleListIds()); + int matchedPlaceholder = 0; + for (FaceSampleRespVO faceSample : faceSampleList) { + if (placeholderList.contains(faceSample.getDeviceId().toString())) { + matchedPlaceholder += 1; + } + } + if (matchedPlaceholder >= minimalPlaceholderFill) { + log.info("当前人脸样本{}已超过最小占位素材{},自动创建任务", face.getFaceUrl(), minimalPlaceholderFill); + taskService.createRenderTask(scenic.getId(), template.getId(), face.getId()); + faceMapper.finishedJourney(face.getId()); + } else { + log.info("当前人脸样本{}未超过最小占位素材{},未达到自动生成条件", face.getFaceUrl(), minimalPlaceholderFill); + } + } + } + } + } + } +} diff --git a/src/main/java/com/ycwl/basic/task/FaceCleaner.java b/src/main/java/com/ycwl/basic/task/FaceCleaner.java new file mode 100644 index 0000000..14c4b22 --- /dev/null +++ b/src/main/java/com/ycwl/basic/task/FaceCleaner.java @@ -0,0 +1,35 @@ +package com.ycwl.basic.task; + +import com.ycwl.basic.mapper.pc.FaceSampleMapper; +import com.ycwl.basic.mapper.pc.ScenicMapper; +import com.ycwl.basic.model.pc.scenic.req.ScenicReqQuery; +import com.ycwl.basic.model.pc.scenic.resp.ScenicRespVO; +import com.ycwl.basic.service.task.TaskFaceService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@EnableScheduling +@Slf4j +public class FaceCleaner { + @Autowired + private ScenicMapper scenicMapper; + @Autowired + private TaskFaceService faceService; + + + @Scheduled(cron = "0 0 4 * * ?") + public void clean(){ + ScenicReqQuery scenicQuery = new ScenicReqQuery(); + List scenicList = scenicMapper.list(scenicQuery); + scenicList.forEach(scenic -> { + log.info("当前景区{},开始删除人脸样本", scenic.getName()); + faceService.batchDeleteFace(scenic.getId()); + }); + } +} diff --git a/src/main/java/com/ycwl/basic/task/GetSpaceChinaMobileLiveSteamJob.java b/src/main/java/com/ycwl/basic/task/GetSpaceChinaMobileLiveSteamJob.java deleted file mode 100644 index 4740bff..0000000 --- a/src/main/java/com/ycwl/basic/task/GetSpaceChinaMobileLiveSteamJob.java +++ /dev/null @@ -1,176 +0,0 @@ -//package com.ycwl.basic.task; -// -// -//import cn.hutool.core.util.StrUtil; -//import com.alibaba.fastjson.JSONArray; -//import com.alibaba.fastjson.JSONObject; -//import com.tu.common.redis.RedisUtils; -//import com.tu.common.utils.ChinaMobileUtil; -//import com.tu.common.utils.DateUtils; -//import com.tu.dao.TuSpaceManageDao; -//import com.tu.dto.GetLiveSteamUrlDTO; -//import lombok.extern.slf4j.Slf4j; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.beans.factory.annotation.Value; -//import org.springframework.core.annotation.Order; -//import org.springframework.scheduling.annotation.EnableScheduling; -//import org.springframework.scheduling.annotation.Scheduled; -//import org.springframework.stereotype.Component; -// -//import javax.annotation.PostConstruct; -//import java.util.*; -// -//import static com.tu.common.utils.ChinaMobileUtil.getTokenBySend; -//import static com.tu.common.utils.ChinaMobileUtil.sendPost; -// -//@Slf4j -//@Component -//@EnableScheduling -//public class GetSpaceChinaMobileLiveSteamJob { -// -// @Autowired -// TuSpaceManageDao tuSpaceManageDao; -// @Autowired -// RedisUtils redisUtils; -// @Autowired -// ChinaMobileUtil chinaMobileUtil; -// @Value("${spring.profiles.active}") -// private String springProfile; -// -// //public static String token = null; -// private static Queue failMsgSubQueue = new LinkedList(); // 保存订阅失败的数据 -// -// /** -// * 订阅消息 -// */ -// @PostConstruct -// public void msgSubscription() { -// new Thread(() -> { -// if (springProfile.equals("prod")) { -// List liveSteamUrl = tuSpaceManageDao.getLiveSteamUrl(null, 2); -// List deviceIdList = new ArrayList<>(); -// for (GetLiveSteamUrlDTO item : liveSteamUrl) { -// String deviceSn = item.getDeviceSn(); -// if (deviceSn != null && !deviceSn.equals("")) { -// deviceIdList.add(deviceSn); -// -// if (deviceIdList.size() == 29) { -// // 30条数据为一组,分开订阅 -// String msg = doMsgSubscription(deviceIdList); -// if (StrUtil.isBlank(msg)) { -// // 消息订阅失败,过一会儿再试试 -// List failDeviceIdList = new ArrayList<>(); -// Collections.copy(failDeviceIdList, deviceIdList); -// failMsgSubQueue.offer(failDeviceIdList); -// } -// deviceIdList = new ArrayList<>(); -// } -// -// } -// } -// doMsgSubscription(deviceIdList); -// } -// }).start(); -// -// } -// -// -// /** -// * 每小时扫描订阅失败的消息 -// */ -// @PostConstruct -// @Scheduled(cron = "0 0 * * * *") -// public void scanQueue() { -// if (failMsgSubQueue.size() > 0) { -// Object poll = failMsgSubQueue.poll(); -// if (poll != null) { -// List pollList = (List) poll; -// String msg = doMsgSubscription(pollList); -// if (StrUtil.isBlank(msg)) { -// // 消息订阅失败,过一会儿再试试 -// List failDeviceIdList = new ArrayList<>(); -// Collections.copy(failDeviceIdList, pollList); -// failMsgSubQueue.offer(failDeviceIdList); -// } -// } -// } -// } -// -// -// @PostConstruct -// @Scheduled(fixedRate = 1000 * 60 * 60 * 2) -// @Order(1) -// public void getToken() { -// if (springProfile.equals("prod")) { -// String tokenBySend = getTokenBySend(); -// redisUtils.set("CHINA_MOBILE_TOKEN_KEY_NEW", tokenBySend, RedisUtils.HOUR_TOW_EXPIRE); -// } -// } -// -// @Scheduled(cron = "0 */19 * * * *") -// @PostConstruct -// @Order(2) -// public void setLiveSteam() { -// new Thread(() -> { -// if (springProfile.equals("prod")) { -// List liveSteamUrl = tuSpaceManageDao.getLiveSteamUrl(null, 2); -// for (GetLiveSteamUrlDTO item : liveSteamUrl) { -// String url = getLiveSteam(item.getDeviceSn()); -// if (url != null && !url.equals("")) { -// tuSpaceManageDao.updateLiveSteamUrl(item.getId(), url); -// } -// } -// } -// }).start(); -// } -// -// public String getLiveSteam(String deviceId) { -// log.info("【移动】开始获取视频流" + deviceId); -// String url_hls = "https://open.andmu.cn/v3/open/api/device/hls"; -// JSONObject bodyParam = new JSONObject(); -// bodyParam.put("deviceId", deviceId); -// bodyParam.put("endTime", DateUtils.addDateDays(new Date(), 2).getTime()); -// log.info("【移动】bodyParam->{}", bodyParam); -// String res = sendPost(url_hls, bodyParam, chinaMobileUtil.getTempToken()); -// log.info("【移动】开始获取视频流结果:-》{}" + res); -// JSONObject jsonObject = JSONObject.parseObject(res); -// String m3u8Url = null; -// if (jsonObject.get("resultCode").toString().equals("000000")) { -// String data = jsonObject.get("data").toString(); -// JSONObject jsonData = JSONObject.parseObject(data); -// m3u8Url = jsonData.get("m3u8Url").toString(); -// } -// // token已过期 -// if (jsonObject.get("resultCode").toString().equals("11504")) { -// // 删除redis的token,会重新获取 -// redisUtils.delete("CHINA_MOBILE_TOKEN_KEY_NEW"); -// getLiveSteam(deviceId); -// } -// return m3u8Url; -// } -// -// -// public static void main(String[] args) { -// -// //List deviceIdList =new ArrayList<>(30); -// //deviceIdList.add("2"); -// //deviceIdList.add("1"); -// // -// //List failDeviceIdList = new ArrayList<>(deviceIdList); -// //failMsgSubQueue.offer(failDeviceIdList); -// //deviceIdList = new ArrayList<>(); -// //List pollList = ( List) failMsgSubQueue.poll(); -// //for (String s : pollList) { -// // System.out.println(s); -// //} -// //System.out.println(failMsgSubQueue.poll()); -// //System.out.println(failMsgSubQueue.size()); -// -// -// //String s = doMsgSubscription(Arrays.asList("040312e7fc9f", "743fc2dae410")); -// //System.out.println(s); -// } -// -// -// -//} diff --git a/src/main/java/com/ycwl/basic/task/VideoPieceCleaner.java b/src/main/java/com/ycwl/basic/task/VideoPieceCleaner.java new file mode 100644 index 0000000..aa44056 --- /dev/null +++ b/src/main/java/com/ycwl/basic/task/VideoPieceCleaner.java @@ -0,0 +1,49 @@ +package com.ycwl.basic.task; + + +import com.ycwl.basic.device.DeviceFactory; +import com.ycwl.basic.device.operator.IDeviceStorageOperator; +import com.ycwl.basic.mapper.pc.DeviceMapper; +import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; +import com.ycwl.basic.model.pc.device.req.DeviceReqQuery; +import com.ycwl.basic.model.pc.device.resp.DeviceRespVO; +import lombok.extern.slf4j.Slf4j; +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.List; + +@Component +@EnableScheduling +@Slf4j +public class VideoPieceCleaner { + @Autowired + private DeviceMapper deviceMapper; + + @Scheduled(cron = "0 0 0 * * ?") + public void clean() { + log.info("开始删除视频文件"); + List deviceList = deviceMapper.list(new DeviceReqQuery()); + for (DeviceRespVO device : deviceList) { + DeviceConfigEntity config = deviceMapper.getConfigByDeviceId(device.getId()); + if (config == null) { + continue; + } + if (config.getStoreExpireDay() == null) { + continue; + } + if (config.getStoreExpireDay() <= 0) { + continue; + } + IDeviceStorageOperator storageOperator = DeviceFactory.getDeviceStorageOperator(null, config); + if (storageOperator == null) { + continue; + } + storageOperator.removeFilesBeforeDate(new Date(System.currentTimeMillis() - config.getStoreExpireDay() * 24 * 60 * 60 * 1000)); + log.info("删除视频文件完成"); + } + } +} diff --git a/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java b/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java new file mode 100644 index 0000000..43fe52d --- /dev/null +++ b/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java @@ -0,0 +1,370 @@ +package com.ycwl.basic.task; + +import cn.hutool.core.date.DateUtil; +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.mapper.pc.DeviceMapper; +import com.ycwl.basic.mapper.pc.FaceSampleMapper; +import com.ycwl.basic.mapper.pc.SourceMapper; +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.SourceEntity; +import com.ycwl.basic.utils.OssUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +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.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Component +@EnableScheduling +@Slf4j +public class VideoPieceGetter { + @Autowired + private FaceSampleMapper faceSampleMapper; + @Autowired + private DeviceMapper deviceMapper; + @Autowired + private OssUtil ossUtil; + @Autowired + private SourceMapper sourceMapper; + + @Data + public static class Task { + public Long deviceId; + public Long faceSampleId; + public Date createTime; + } + @Data + public static class FfmpegTask { + List fileList; + BigDecimal duration; + BigDecimal offsetStart; + String outputFile; + } + + public static LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); + + public static void addTask(Task task) { + queue.add(task); + } + + @Scheduled(fixedDelay = 5000L) + public void doTask() { + Task task = queue.poll(); + if (task == null) { + return; + } + log.info("poll task: {}", task); + FaceSampleRespVO faceSample = faceSampleMapper.getById(task.getFaceSampleId()); + DeviceEntity device = deviceMapper.getByDeviceId(task.getDeviceId()); + DeviceConfigEntity config = deviceMapper.getConfigByDeviceId(task.getDeviceId()); + BigDecimal cutPre = BigDecimal.valueOf(5L); + BigDecimal cutPost = BigDecimal.valueOf(4L); + if (config == null) { + return; + } + // 有配置 + if (config.getCutPre() != null) { + cutPre = config.getCutPre(); + } + if (config.getCutPost() != null) { + cutPost = config.getCutPost(); + } + IDeviceStorageOperator pieceGetter = DeviceFactory.getDeviceStorageOperator(device, config); + if (pieceGetter == null) { + return; + } + BigDecimal duration = cutPre.add(cutPost); + List listByDtRange = pieceGetter.getFileListByDtRange( + new Date(task.getCreateTime().getTime() - cutPre.multiply(BigDecimal.valueOf(1000)).longValue()), + new Date(task.getCreateTime().getTime() + cutPost.multiply(BigDecimal.valueOf(1000)).longValue()) + ); + if (listByDtRange.isEmpty()) { + queue.add(task); + return; + } + long offset = task.getCreateTime().getTime() - 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("视频裁切成功"); + try { + InputStream inputStream = new FileInputStream(outFile); + String url = ossUtil.uploadFile(inputStream, "user-video-source", outFile.getName()); + SourceEntity sourceEntity = new SourceEntity(); + sourceEntity.setVideoUrl(url); + sourceEntity.setFaceSampleId(faceSample.getId()); + sourceEntity.setScenicId(faceSample.getScenicId()); + sourceEntity.setDeviceId(faceSample.getDeviceId()); + sourceEntity.setType(1); + sourceMapper.add(sourceEntity); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + public boolean startFfmpegTask(FfmpegTask task) { + boolean result; + if (task.getFileList().size() == 1) { + // 单个文件切割,用简单方法 + result = runFfmpegForSingleFile(task); + } else { + // 多个文件切割,用速度快的 + result = runFfmpegForMultipleFile1(task); + } + // 先尝试方法1 + if (result) { + return true; + } + log.warn("FFMPEG简易方法失败,尝试复杂方法转码"); + // 不行再尝试方法二 + return runFfmpegForMultipleFile2(task); + } + + private boolean runFfmpegForMultipleFile1(FfmpegTask task) { + // 多文件,方法一:先转换成ts,然后合并切割 + // 步骤一:先转换成ts,并行转换 + boolean notOk = task.getFileList().stream().map(file -> { + try { + if (file.isNeedDownload() || (!file.getName().endsWith(".ts"))) { + String tmpFile = file.getName() + ".ts"; + boolean result = convertMp4ToTs(file, tmpFile); + // 因为是并行转换,没法保证顺序,就直接存里面 + if (result) { + file.setUrl(tmpFile); + } else { + // 失败了,务必删除临时文件 + (new File(tmpFile)).delete(); + } + return result; + } else { + return true; + } + } catch (IOException e) { + log.warn("转码出错"); + return false; + } + }).anyMatch(b -> !b); + // 转码进程中出现问题 + if (notOk) { + return false; + } + // 步骤二:使用concat协议拼接裁切 + boolean result; + try { + result = quickVideoCut( + "concat:" + task.getFileList().stream().map(FileObject::getUrl).collect(Collectors.joining("|")), + task.getOffsetStart(), task.getDuration(), task.getOutputFile() + ); + } catch (IOException e) { + return false; + } + // 步骤三:删除临时文件 + task.getFileList().stream().map(FileObject::getUrl).forEach(tmpFile -> { + File f = new File(tmpFile); + if (f.exists() && f.isFile()) { + f.delete(); + } + }); + return result; + } + + private boolean runFfmpegForMultipleFile2(FfmpegTask task) { + // 多文件,方法二:使用计算资源编码 + try { + return slowVideoCut(task.getFileList(), task.getOffsetStart(), task.getDuration(), task.getOutputFile()); + } catch (IOException e) { + return false; + } + } + + private boolean runFfmpegForSingleFile(FfmpegTask task) { + try { + return quickVideoCut(task.getFileList().get(0).getUrl(), task.getOffsetStart(), task.getDuration(), task.getOutputFile()); + } catch (IOException e) { + return false; + } + } + + /** + * 把MP4转换成可以拼接的TS文件 + * + * @param file MP4文件,或ffmpeg支持的输入 + * @param outFileName 输出文件路径 + * @return 是否成功 + * @throws IOException 奇奇怪怪的报错 + */ + private boolean convertMp4ToTs(FileObject file, String outFileName) throws IOException { + List ffmpegCmd = new ArrayList<>(); + ffmpegCmd.add("ffmpeg"); + ffmpegCmd.add("-hide_banner"); + ffmpegCmd.add("-y"); + ffmpegCmd.add("-i"); + ffmpegCmd.add(file.getUrl()); + ffmpegCmd.add("-c"); + ffmpegCmd.add("copy"); + ffmpegCmd.add("-bsf:v"); + ffmpegCmd.add("h264_mp4toannexb"); + ffmpegCmd.add("-f"); + ffmpegCmd.add("mpegts"); + ffmpegCmd.add(outFileName); + return handleFfmpegProcess(ffmpegCmd); + } + private boolean convertHevcToTs(FileObject file, String outFileName) throws IOException { + List ffmpegCmd = new ArrayList<>(); + ffmpegCmd.add("ffmpeg"); + ffmpegCmd.add("-hide_banner"); + ffmpegCmd.add("-y"); + ffmpegCmd.add("-i"); + ffmpegCmd.add(file.getUrl()); + ffmpegCmd.add("-c"); + ffmpegCmd.add("copy"); + ffmpegCmd.add("-bsf:v"); + ffmpegCmd.add("hevc_mp4toannexb"); + ffmpegCmd.add("-f"); + ffmpegCmd.add("mpegts"); + ffmpegCmd.add(outFileName); + return handleFfmpegProcess(ffmpegCmd); + } + + /** + * 快速切割,不产生转码,速度快,但可能会出现:第一帧数据不是I帧导致前面的数据无法使用 + * + * @param inputFile 输入文件,ffmpeg支持的协议均可 + * @param offset 离输入文件开始的偏移 + * @param length 输出文件时长 + * @param outputFile 输出文件名称 + * @return 是否成功 + * @throws IOException 奇奇怪怪的报错 + */ + private boolean quickVideoCut(String inputFile, BigDecimal offset, BigDecimal length, String outputFile) throws IOException { + List ffmpegCmd = new ArrayList<>(); + ffmpegCmd.add("ffmpeg"); + ffmpegCmd.add("-hide_banner"); + ffmpegCmd.add("-y"); + ffmpegCmd.add("-i"); + ffmpegCmd.add(inputFile); + ffmpegCmd.add("-c:v"); + ffmpegCmd.add("copy"); + ffmpegCmd.add("-an"); + ffmpegCmd.add("-ss"); + ffmpegCmd.add(offset.toPlainString()); + ffmpegCmd.add("-t"); + ffmpegCmd.add(length.toPlainString()); + ffmpegCmd.add("-f"); + ffmpegCmd.add("mp4"); + ffmpegCmd.add(outputFile); + return handleFfmpegProcess(ffmpegCmd); + } + + /** + * 转码切割,兜底逻辑,速度慢,但优势:成功后转码视频绝对可用 + * + * @param inputFiles 输入文件List,ffmpeg支持的协议均可 + * @param offset 离输入文件开始的偏移 + * @param length 输出文件时长 + * @param outputFile 输出文件名称 + * @return 是否成功 + * @throws IOException 奇奇怪怪的报错 + */ + private boolean slowVideoCut(List inputFiles, BigDecimal offset, BigDecimal length, String outputFile) throws IOException { + List ffmpegCmd = new ArrayList<>(); + ffmpegCmd.add("ffmpeg"); + ffmpegCmd.add("-hide_banner"); + ffmpegCmd.add("-y"); + for (FileObject file : inputFiles) { + ffmpegCmd.add("-i"); + ffmpegCmd.add(file.getUrl()); + } + // 使用filter_complex做拼接 + ffmpegCmd.add("-filter_complex"); + ffmpegCmd.add( + IntStream.range(0, inputFiles.size()).mapToObj(i -> "[" + i + ":v]").collect(Collectors.joining("")) + + "concat=n=2:v=1[v]" + ); + ffmpegCmd.add("-map"); + ffmpegCmd.add("[v]"); + ffmpegCmd.add("-preset:v"); + ffmpegCmd.add("fast"); + ffmpegCmd.add("-an"); + // 没有使用copy,因为使用了filter_complex + ffmpegCmd.add("-ss"); + ffmpegCmd.add(offset.toPlainString()); + ffmpegCmd.add("-t"); + ffmpegCmd.add(length.toPlainString()); + ffmpegCmd.add("-f"); + ffmpegCmd.add("mp4"); + ffmpegCmd.add(outputFile); + + return handleFfmpegProcess(ffmpegCmd); + } + + /** + * 运行ffmpeg,并确认ffmpeg是否正常退出 + * + * @param ffmpegCmd ffmpeg命令 + * @return 是否正常退出 + */ + private static boolean handleFfmpegProcess(List ffmpegCmd) throws IOException { + Date _startDt = new Date(); + log.info("FFMPEG执行命令:【{}】", String.join(" ", ffmpegCmd)); + ProcessBuilder pb = new ProcessBuilder(ffmpegCmd); + Process ffmpegProcess = pb.start(); + // 如果需要额外分析输出之类 + if (log.isTraceEnabled()) { + InputStream stderr = ffmpegProcess.getErrorStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stderr)); + String line; + while ((line = reader.readLine()) != null) { + log.trace(line); + } + } + try { + // 最长1分钟 + boolean exited = ffmpegProcess.waitFor(1, TimeUnit.MINUTES); + if (exited) { + int code = ffmpegProcess.exitValue(); + Date _endDt = new Date(); + log.info("FFMPEG执行命令结束,Code:【{}】,耗费时间:【{}ms】,命令:【{}】", code, _endDt.getTime() - _startDt.getTime(), String.join(" ", ffmpegCmd)); + return 0 == code; + } else { + log.error("FFMPEG执行命令没有在1分钟内退出,命令:【{}】", String.join(" ", ffmpegCmd)); + ffmpegProcess.destroy(); + return false; + } + } catch (InterruptedException e) { + // TODO: 被中断了 + log.warn("FFMPEG执行命令:【{}】,被中断了", String.join(" ", ffmpegCmd)); + return false; + } + } + +} diff --git a/src/main/resources/mapper/pc/DeviceMapper.xml b/src/main/resources/mapper/pc/DeviceMapper.xml index d805506..1f3a26b 100644 --- a/src/main/resources/mapper/pc/DeviceMapper.xml +++ b/src/main/resources/mapper/pc/DeviceMapper.xml @@ -72,4 +72,14 @@ group by device_id )b on a.scenic_id = b.scenic_id + + \ No newline at end of file diff --git a/src/main/resources/mapper/pc/FaceMapper.xml b/src/main/resources/mapper/pc/FaceMapper.xml index c74d2f1..b46b00d 100644 --- a/src/main/resources/mapper/pc/FaceMapper.xml +++ b/src/main/resources/mapper/pc/FaceMapper.xml @@ -2,12 +2,15 @@ - insert into face(id, score, member_id, face_url, match_sample_ids, first_match_rate, match_result) - values (#{id}, #{score}, #{memberId}, #{faceUrl}, #{matchSampleIds}, #{firstMatchRate}, #{matchResult}) + insert into face(id, scenic_id, score, member_id, face_url, match_sample_ids, first_match_rate, match_result) + values (#{id}, #{scenicId}, #{score}, #{memberId}, #{faceUrl}, #{matchSampleIds}, #{firstMatchRate}, #{matchResult}) update face + + scenic_id = #{scenicId}, + member_id = #{memberId}, @@ -29,6 +32,9 @@ where id = #{id} + + update face set finished_journey = 1 where id = #{id} + delete from face where id = #{id} @@ -42,7 +48,7 @@ + \ No newline at end of file diff --git a/src/main/resources/mapper/pc/FaceSampleMapper.xml b/src/main/resources/mapper/pc/FaceSampleMapper.xml index 649539c..0cba491 100644 --- a/src/main/resources/mapper/pc/FaceSampleMapper.xml +++ b/src/main/resources/mapper/pc/FaceSampleMapper.xml @@ -85,4 +85,13 @@ from face_sample where id = #{id} + \ No newline at end of file diff --git a/src/main/resources/mapper/pc/OrderMapper.xml b/src/main/resources/mapper/pc/OrderMapper.xml index 7429bbf..4c0695c 100644 --- a/src/main/resources/mapper/pc/OrderMapper.xml +++ b/src/main/resources/mapper/pc/OrderMapper.xml @@ -3,6 +3,8 @@ + + @@ -120,10 +122,11 @@ delete from `order` where id = #{id} + - select s.id, `name`, `phone`, introduction,cover_url, longitude, latitude, radius, province, city, area, address, `status`, s.create_time, update_time, - c.start_time, c.end_time, c.is_default, c.create_time createTime2,s.price + s.price from scenic s - left join scenic_config c on s.id = c.id where s.id = #{id} + - + @@ -194,13 +196,5 @@ - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/mapper/pc/TaskMapper.xml b/src/main/resources/mapper/pc/TaskMapper.xml index 7f67abb..23d21dd 100644 --- a/src/main/resources/mapper/pc/TaskMapper.xml +++ b/src/main/resources/mapper/pc/TaskMapper.xml @@ -10,6 +10,7 @@ worker_id = #{workerId}, member_id = #{memberId}, + face_id = #{faceId}, template_id = #{templateId}, scenic_id = #{scenicId}, task_params = #{taskParams}, @@ -26,7 +27,7 @@ update task - set worker_id = #{workerId} + set worker_id = #{workerId}, status = 2 where id = #{taskId} diff --git a/src/main/resources/mapper/pc/TemplateMapper.xml b/src/main/resources/mapper/pc/TemplateMapper.xml index 8cd6bb9..4d039ac 100644 --- a/src/main/resources/mapper/pc/TemplateMapper.xml +++ b/src/main/resources/mapper/pc/TemplateMapper.xml @@ -5,6 +5,10 @@ insert into template(id, scenic_id, `name`, pid, is_placeholder, source_url, luts, overlays, audios, cover_url, frame_rate, speed, price) values (#{id}, #{scenicId}, #{name}, #{pid}, #{isPlaceholder}, #{sourceUrl}, #{luts}, #{overlays}, #{audios}, #{coverUrl}, #{frameRate}, #{speed}, #{price}) + + insert into template_config(template_id, is_default, minimal_placeholder_fill) + values (#{templateId}, #{isDefault}, #{minimalPlaceholderFill}) + update template @@ -35,6 +39,14 @@ END) where id = #{id} + + update template_config + + is_default = #{isDefault}, + minimal_placeholder_fill = #{minimalPlaceholderFill}, + + where id = #{id} + delete from template where id = #{id} @@ -44,6 +56,12 @@ delete from template where scenic_id = #{id} + + delete from template_config where template_id = #{id} + + + delete from template_config where id = #{id} + + \ No newline at end of file diff --git a/src/main/resources/mapper/pc/VideoMapper.xml b/src/main/resources/mapper/pc/VideoMapper.xml index 518e7ef..c25afa1 100644 --- a/src/main/resources/mapper/pc/VideoMapper.xml +++ b/src/main/resources/mapper/pc/VideoMapper.xml @@ -49,4 +49,7 @@ left join template t on v.template_id = t.id where v.id = #{id} + \ No newline at end of file