设备关联逻辑

This commit is contained in:
Jerry Yan 2025-03-19 16:57:33 +08:00
parent fb530a1eb4
commit bcf8e8e88d
3 changed files with 167 additions and 113 deletions

View File

@ -60,4 +60,5 @@ public class DeviceConfigEntity {
private Integer enablePreBook; private Integer enablePreBook;
private Integer imageFree; private Integer imageFree;
private Integer videoFree; private Integer videoFree;
private Long pairDevice;
} }

View File

@ -6,6 +6,7 @@ import com.ycwl.basic.device.DeviceFactory;
import com.ycwl.basic.device.entity.common.FileObject; import com.ycwl.basic.device.entity.common.FileObject;
import com.ycwl.basic.device.operator.IDeviceStorageOperator; import com.ycwl.basic.device.operator.IDeviceStorageOperator;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity; import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
import com.ycwl.basic.repository.DeviceRepository; import com.ycwl.basic.repository.DeviceRepository;
import com.ycwl.basic.mapper.FaceSampleMapper; import com.ycwl.basic.mapper.FaceSampleMapper;
import com.ycwl.basic.mapper.SourceMapper; import com.ycwl.basic.mapper.SourceMapper;
@ -20,6 +21,7 @@ import com.ycwl.basic.storage.adapters.IStorageAdapter;
import com.ycwl.basic.utils.SnowFlakeUtil; import com.ycwl.basic.utils.SnowFlakeUtil;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
@ -35,7 +37,9 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -117,6 +121,18 @@ public class VideoPieceGetter {
final ThreadPoolExecutor executor = new ThreadPoolExecutor(16, 512, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(512)); final ThreadPoolExecutor executor = new ThreadPoolExecutor(16, 512, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(512));
List<String> currentPlaceholder = new ArrayList<>(); List<String> currentPlaceholder = new ArrayList<>();
List<FaceSampleEntity> list = faceSampleMapper.listByIds(task.getFaceSampleIds()); List<FaceSampleEntity> list = faceSampleMapper.listByIds(task.getFaceSampleIds());
Map<Long, Long> pairDeviceMap = new ConcurrentHashMap<>();
if (!list.isEmpty()) {
Long scenicId = list.get(0).getScenicId();
List<DeviceEntity> allDeviceByScenicId = deviceRepository.getAllDeviceByScenicId(scenicId);
allDeviceByScenicId.forEach(device -> {
Long deviceId = device.getId();
DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(deviceId);
if (deviceConfig != null && deviceConfig.getPairDevice() != null) {
pairDeviceMap.putIfAbsent(deviceId, deviceConfig.getPairDevice());
}
});
}
Collection<List<FaceSampleEntity>> collection = list.stream() Collection<List<FaceSampleEntity>> collection = list.stream()
.filter(faceSample -> { .filter(faceSample -> {
if (templatePlaceholder != null) { if (templatePlaceholder != null) {
@ -136,125 +152,47 @@ public class VideoPieceGetter {
} catch (InterruptedException ignore) { } catch (InterruptedException ignore) {
} }
} }
isFirst.set(false); if (pairDeviceMap.containsValue(faceSample.getDeviceId())) {
// 有关联设备
// 找到对应的deviceId
pairDeviceMap.entrySet().stream()
.filter(entry -> entry.getValue().equals(faceSample.getDeviceId()))
.map(Map.Entry::getKey).forEach(pairDeviceId -> {
log.info("找到同景区关联设备:{} -> {}", pairDeviceId, faceSample.getDeviceId());
if (pairDeviceId != null) {
executor.execute(() -> { executor.execute(() -> {
DeviceEntity device = deviceRepository.getDevice(faceSample.getDeviceId()); boolean result = doCut(pairDeviceId, faceSample.getId(), faceSample.getCreateAt(), task);
DeviceConfigEntity config = deviceRepository.getDeviceConfig(faceSample.getDeviceId());
SourceEntity source = sourceMapper.querySameVideo(faceSample.getId(), device.getId());
if (source == null) {
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<FileObject> listByDtRange = pieceGetter.getFileListByDtRange(
new Date(faceSample.getCreateAt().getTime() - cutPre.multiply(BigDecimal.valueOf(1000)).longValue()),
new Date(faceSample.getCreateAt().getTime() + cutPost.multiply(BigDecimal.valueOf(1000)).longValue())
);
if (listByDtRange.isEmpty()) {
log.warn("没有可用的文件");
return;
}
log.info("查询到可用的文件: {}", listByDtRange);
// 如果完全一致就不需要裁切
String url;
if (listByDtRange.size() == 1 && listByDtRange.get(0).isExact()) {
url = listByDtRange.get(0).getUrl();
} else {
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();
}
SourceEntity imgSource = sourceMapper.findBySampleId(faceSample.getId());
SourceEntity sourceEntity = new SourceEntity();
sourceEntity.setId(SnowFlakeUtil.getLongId());
sourceEntity.setCreateTime(faceSample.getCreateAt());
if (imgSource != null) {
sourceEntity.setUrl(imgSource.getUrl());
sourceEntity.setPosJson(imgSource.getPosJson());
}
sourceEntity.setVideoUrl(url);
sourceEntity.setFaceSampleId(faceSample.getId());
sourceEntity.setScenicId(faceSample.getScenicId());
sourceEntity.setDeviceId(faceSample.getDeviceId());
sourceEntity.setType(1);
if (task.memberId != null && task.faceId != null) {
MemberSourceEntity videoSource = new MemberSourceEntity();
videoSource.setMemberId(task.getMemberId());
videoSource.setType(1);
videoSource.setFaceId(task.getFaceId());
videoSource.setScenicId(faceSample.getScenicId());
videoSource.setSourceId(sourceEntity.getId());
IsBuyRespVO isBuy = orderBiz.isBuy(task.getMemberId(), faceSample.getScenicId(), 1, task.getFaceId());
if (isBuy.isBuy()) { // 如果用户买过
videoSource.setIsBuy(1);
} else if (isBuy.isFree()) { // 全免费逻辑
videoSource.setIsBuy(1);
} else {
videoSource.setIsBuy(0);
}
sourceMapper.addRelation(videoSource);
}
sourceMapper.add(sourceEntity);
} else {
// 有原视频
if (task.memberId != null && task.faceId != null) {
int count = sourceMapper.hasRelationTo(task.getMemberId(), source.getId(), 1);
if (count <= 0) {
// 没有关联
IsBuyRespVO isBuy = orderBiz.isBuy(task.getMemberId(), faceSample.getScenicId(), 1, task.getFaceId());
MemberSourceEntity videoSource = new MemberSourceEntity();
videoSource.setId(SnowFlakeUtil.getLongId());
videoSource.setScenicId(faceSample.getScenicId());
videoSource.setFaceId(task.getFaceId());
videoSource.setMemberId(task.getMemberId());
videoSource.setType(1);
if (isBuy.isBuy()) { // 如果用户买过
videoSource.setIsBuy(1);
} else if (isBuy.isFree()) { // 全免费逻辑
videoSource.setIsBuy(1);
} else {
videoSource.setIsBuy(0);
}
videoSource.setSourceId(source.getId());
sourceMapper.addRelation(videoSource);
}
}
}
if (templatePlaceholder != null) { if (templatePlaceholder != null) {
if (templatePlaceholder.contains(faceSample.getDeviceId().toString())) { if (templatePlaceholder.contains(faceSample.getDeviceId().toString())) {
if (!currentPlaceholder.contains(faceSample.getDeviceId().toString())) { if (!currentPlaceholder.contains(faceSample.getDeviceId().toString())) {
if (result) {
currentPlaceholder.add(faceSample.getDeviceId().toString()); currentPlaceholder.add(faceSample.getDeviceId().toString());
} }
} }
}
log.info("当前进度:{}/{}", currentPlaceholder.size(), collection.size());
if (currentPlaceholder.size() >= collection.size()) {
if (!invoke.get()) {
invoke.set(true);
task.getCallback().onInvoke();
}
}
}
});
}
});
}
isFirst.set(false);
executor.execute(() -> {
boolean result = doCut(faceSample.getDeviceId(), faceSample.getId(), faceSample.getCreateAt(), task);
if (templatePlaceholder != null) {
if (templatePlaceholder.contains(faceSample.getDeviceId().toString())) {
if (!currentPlaceholder.contains(faceSample.getDeviceId().toString())) {
if (result) {
currentPlaceholder.add(faceSample.getDeviceId().toString());
}
}
}
log.info("当前进度:{}/{}", currentPlaceholder.size(), collection.size()); log.info("当前进度:{}/{}", currentPlaceholder.size(), collection.size());
if (currentPlaceholder.size() >= collection.size()) { if (currentPlaceholder.size() >= collection.size()) {
if (!invoke.get()) { if (!invoke.get()) {
@ -288,6 +226,120 @@ public class VideoPieceGetter {
} }
} }
private boolean doCut(Long deviceId, Long faceSampleId, Date baseTime, Task task) {
DeviceEntity device = deviceRepository.getDevice(deviceId);
DeviceConfigEntity config = deviceRepository.getDeviceConfig(deviceId);
SourceEntity source = sourceMapper.querySameVideo(faceSampleId, device.getId());
if (source == null) {
BigDecimal cutPre = BigDecimal.valueOf(5L);
BigDecimal cutPost = BigDecimal.valueOf(4L);
if (config != null) {
// 有配置
if (config.getCutPre() != null) {
cutPre = config.getCutPre();
}
if (config.getCutPost() != null) {
cutPost = config.getCutPost();
}
}
IDeviceStorageOperator pieceGetter = DeviceFactory.getDeviceStorageOperator(device, config);
if (pieceGetter == null) {
return false;
}
BigDecimal duration = cutPre.add(cutPost);
List<FileObject> listByDtRange = pieceGetter.getFileListByDtRange(
new Date(baseTime.getTime() - cutPre.multiply(BigDecimal.valueOf(1000)).longValue()),
new Date(baseTime.getTime() + cutPost.multiply(BigDecimal.valueOf(1000)).longValue())
);
if (listByDtRange.isEmpty()) {
log.warn("没有可用的文件");
return false;
}
log.info("查询到可用的文件: {}", listByDtRange);
// 如果完全一致就不需要裁切
String url;
if (listByDtRange.size() == 1 && listByDtRange.get(0).isExact()) {
url = listByDtRange.get(0).getUrl();
} else {
long offset = baseTime.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(deviceId.toString() + "_" + faceSampleId + ".mp4");
ffmpegTask.setOutputFile(outFile.getAbsolutePath());
boolean result = startFfmpegTask(ffmpegTask);
if (!result) {
log.warn("视频裁切失败");
return false;
}
log.info("视频裁切成功");
IStorageAdapter adapter = StorageFactory.use("assets");
url = adapter.uploadFile(outFile, "video-source", outFile.getName());
// 上传成功后删除文件
outFile.delete();
}
SourceEntity imgSource = sourceMapper.findBySampleId(faceSampleId);
SourceEntity sourceEntity = new SourceEntity();
sourceEntity.setId(SnowFlakeUtil.getLongId());
sourceEntity.setCreateTime(baseTime);
if (imgSource != null) {
sourceEntity.setUrl(imgSource.getUrl());
sourceEntity.setPosJson(imgSource.getPosJson());
}
sourceEntity.setVideoUrl(url);
sourceEntity.setFaceSampleId(faceSampleId);
sourceEntity.setScenicId(device.getScenicId());
sourceEntity.setDeviceId(deviceId);
sourceEntity.setType(1);
if (task.memberId != null && task.faceId != null) {
MemberSourceEntity videoSource = new MemberSourceEntity();
videoSource.setMemberId(task.getMemberId());
videoSource.setType(1);
videoSource.setFaceId(task.getFaceId());
videoSource.setScenicId(device.getScenicId());
videoSource.setSourceId(sourceEntity.getId());
IsBuyRespVO isBuy = orderBiz.isBuy(task.getMemberId(), device.getScenicId(), 1, task.getFaceId());
if (isBuy.isBuy()) { // 如果用户买过
videoSource.setIsBuy(1);
} else if (isBuy.isFree()) { // 全免费逻辑
videoSource.setIsBuy(1);
} else {
videoSource.setIsBuy(0);
}
sourceMapper.addRelation(videoSource);
}
sourceMapper.add(sourceEntity);
} else {
// 有原视频
if (task.memberId != null && task.faceId != null) {
int count = sourceMapper.hasRelationTo(task.getMemberId(), source.getId(), 1);
if (count <= 0) {
// 没有关联
IsBuyRespVO isBuy = orderBiz.isBuy(task.getMemberId(), device.getScenicId(), 1, task.getFaceId());
MemberSourceEntity videoSource = new MemberSourceEntity();
videoSource.setId(SnowFlakeUtil.getLongId());
videoSource.setScenicId(device.getScenicId());
videoSource.setFaceId(task.getFaceId());
videoSource.setMemberId(task.getMemberId());
videoSource.setType(1);
if (isBuy.isBuy()) { // 如果用户买过
videoSource.setIsBuy(1);
} else if (isBuy.isFree()) { // 全免费逻辑
videoSource.setIsBuy(1);
} else {
videoSource.setIsBuy(0);
}
videoSource.setSourceId(source.getId());
sourceMapper.addRelation(videoSource);
}
}
}
return true;
}
public boolean startFfmpegTask(FfmpegTask task) { public boolean startFfmpegTask(FfmpegTask task) {
boolean result; boolean result;
if (task.getFileList().size() == 1) { if (task.getFileList().size() == 1) {

View File

@ -39,7 +39,8 @@
cut_post = #{cutPost}, cut_post = #{cutPost},
enable_pre_book = #{enablePreBook}, enable_pre_book = #{enablePreBook},
image_free = #{imageFree}, image_free = #{imageFree},
video_free = #{videoFree} video_free = #{videoFree},
pair_device = #{pairDevice}
where id = #{id} where id = #{id}
</update> </update>
<update id="updateEntity"> <update id="updateEntity">