rain
2024-07-04 82e4e5f6129be2e691138cd8b8ea3262e64943af
修改播放地址
8 files modified
513 ■■■■ changed files
pom.xml 8 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/controller/LiveStreamController.java 6 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/model/entity/DeviceSetEntity.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/ILiveStreamService.java 3 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java 127 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/service/IFileService.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java 359 ●●●● patch | view | raw | blame | history
src/main/java/com/dji/sample/patches/controller/PatchesController.java 3 ●●●● patch | view | raw | blame | history
pom.xml
@@ -318,14 +318,6 @@
                    </excludes>
                </configuration>
            </plugin>
<!--            <plugin>-->
<!--                <groupId>org.apache.maven.plugins</groupId>-->
<!--                <artifactId>maven-compiler-plugin</artifactId>-->
<!--                <configuration>-->
<!--                    <source>16</source>-->
<!--                    <target>16</target>-->
<!--                </configuration>-->
<!--            </plugin>-->
        </plugins>
        <!--配置后才可以扫描到xml文件-->
src/main/java/com/dji/sample/manage/controller/LiveStreamController.java
@@ -15,6 +15,7 @@
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
@@ -58,7 +59,6 @@
//        CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
        List<CapacityDeviceDTO> liveCapacity = liveStreamService.getLiveCapacity(workspaceId,sn);
        return ResponseResult.success(liveCapacity);
    }
@@ -74,8 +74,8 @@
    }
    @PostMapping("/streams/address")
    public ResponseResult liveAddress(@RequestParam String deviceSn,@RequestParam String deviceId) {
        return liveStreamService.liveAddress(deviceSn,deviceId);
    public ResponseResult liveAddress(@RequestParam String deviceSn,@RequestParam String deviceName) throws IOException {
        return liveStreamService.liveAddress(deviceSn,deviceName);
    }
    /**
src/main/java/com/dji/sample/manage/model/entity/DeviceSetEntity.java
@@ -43,11 +43,10 @@
    @TableField(value = "gbServer_id")
    private String gbServerId;
    @TableField(value = "device_id")
    private String deviceId;
    @TableField(value = "device_name")
    private String deviceName;
    @TableField(value = "workspace_id")
    private String workspaceId;
}
src/main/java/com/dji/sample/manage/service/ILiveStreamService.java
@@ -5,6 +5,7 @@
import com.dji.sample.manage.model.dto.LiveTypeDTO;
import com.dji.sample.manage.model.receiver.LiveCapacityReceiver;
import java.io.IOException;
import java.util.List;
/**
@@ -36,7 +37,7 @@
     * @return
     */
    ResponseResult liveStart(LiveTypeDTO liveParam);
    ResponseResult liveAddress(String deviceSn,String deviceId);
    ResponseResult liveAddress(String deviceSn,String deviceName) throws IOException;
    /**
     * Stop the live streaming by publishing mqtt message.
     * 通过发布mqtt消息来停止实时流。
src/main/java/com/dji/sample/manage/service/impl/LiveStreamServiceImpl.java
@@ -1,5 +1,8 @@
package com.dji.sample.manage.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dji.sample.common.error.LiveErrorEnum;
import com.dji.sample.common.model.ResponseResult;
@@ -19,6 +22,8 @@
import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver;
import com.dji.sample.manage.model.receiver.LiveCapacityReceiver;
import com.dji.sample.manage.service.*;
import com.dji.sample.patches.utils.DistrictCodeUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,6 +31,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -37,8 +43,8 @@
/**
 * @author sean.zhou
 * @date 2021/11/22
 * @version 0.1
 * @date 2021/11/22
 */
@Service
@Transactional
@@ -60,6 +66,7 @@
    @Autowired
    private IDeviceRedisService deviceRedisService;
    @Override
    public List<CapacityDeviceDTO> getLiveCapacity(String workspaceId,String sn) {
@@ -71,7 +78,6 @@
                        .deviceSn(sn)
                        .domains(List.of(DeviceDomainEnum.SUB_DEVICE.getVal(), DeviceDomainEnum.DOCK.getVal()))
                        .build());
        // Query the live capability of each drone.
        return devicesList.stream()
                //过滤出在线设备
@@ -125,15 +131,15 @@
        //相机已经在直播中,请勿重复开启直播
        if(receiveReply.getResult() == 513003) {
            LiveDTO live = new LiveDTO();
            live.setUrl(liveParam.getUrl().replace("rtmp", "https").replace("735","700") + ".flv");
//            LiveUrlGB28181DTO gb28181 = urlToGB28181(liveParam.getUrl());
//            live.setUrl(new StringBuilder()
//                    .append("https://wrj.shuixiongit.com/zb/rtp/")
//                    .append(gb28181.getAgentID())
//                    .append("_")
//                    .append(gb28181.getChannel())
//                    .append(".live.flv")
//                    .toString());
//            live.setUrl(liveParam.getUrl().replace("rtmp", "https").replace("735", "700") + ".flv");
            LiveUrlGB28181DTO gb28181 = urlToGB28181(liveParam.getUrl());
            live.setUrl(new StringBuilder()
                    .append("https://wrj.shuixiongit.com/zb/rtp/")
                    .append(gb28181.getAgentID())
                    .append("_")
                    .append(gb28181.getChannel())
                    .append(".live.flv")
                    .toString());
            return ResponseResult.success(live);
        }
@@ -150,27 +156,27 @@
//                live.setUrl(liveParam.getUrl().replace("rtmp", "webrtc"));
                live.setUrl(liveParam.getUrl().replace("rtmp", "https").replace("735","700") + ".flv");
                break;
            case GB28181:
                LiveUrlGB28181DTO gb28181 = urlToGB28181(liveParam.getUrl());
                live.setUrl(new StringBuilder()
                        .append("webrtc://")
                        .append(gb28181.getServerIP())
                        .append("/live/")
                        .append(gb28181.getAgentID())
                        .append("@")
                        .append(gb28181.getChannel())
                        .toString());
                break;
//            case GB28181:
//                LiveUrlGB28181DTO gb28181 = urlToGB28181(liveParam.getUrl());
//                live.setUrl(new StringBuilder()
//                        .append("https://wrj.shuixiongit.com/zb/rtp/")
//                        .append("webrtc://")
//                        .append(gb28181.getServerIP())
//                        .append("/live/")
//                        .append(gb28181.getAgentID())
//                        .append("_")
//                        .append("@")
//                        .append(gb28181.getChannel())
//                        .append(".live.flv")
//                        .toString());
//                break;
            case GB28181:
                LiveUrlGB28181DTO gb28181 = urlToGB28181(liveParam.getUrl());
                live.setUrl(new StringBuilder()
                        .append("https://wrj.shuixiongit.com/zb/rtp/")
                        .append(gb28181.getAgentID())
                        .append("_")
                        .append(gb28181.getChannel())
                        .append(".live.flv")
                        .toString());
                break;
            case RTSP:
                String url = receiveReply.getOutput().toString();
                this.resolveUrlUser(url, live);
@@ -178,64 +184,50 @@
            case UNKNOWN:
                return ResponseResult.error(LiveErrorEnum.URL_TYPE_NOT_SUPPORTED);
        }
        return ResponseResult.success(live);
    }
    @Override
    public ResponseResult liveAddress(String deviceSn,String deviceId) {
    public ResponseResult liveAddress(String deviceSn, String deviceName) throws IOException {
       DeviceSetEntity deviceSet= deviceSetMapper.selectOne(new LambdaQueryWrapper<DeviceSetEntity>()
                .eq(DeviceSetEntity::getDeviceSn,deviceSn)
                .eq(DeviceSetEntity::getDeviceId,deviceId)
                .eq(DeviceSetEntity::getDeviceName, deviceName)
        );
        String workspaceId=getIdBySn(deviceSn);
        List<CapacityDeviceDTO> dto=getLiveCapacity(workspaceId,deviceSn);
        String vedioId=deviceSn+"/165-0-7/normal-0";
        String url="https://"+deviceSet.getServerIp()+"/zb/rtp/"+deviceSet.getAgentId()+"_"+deviceSet.getChannel()+".live.flv";
        String url = "serverIP=" + deviceSet.getServerIp() + "&serverPort=" + deviceSet.getServerPort() + "&agentID=" + deviceSet.getAgentId()
                + "&agentPassword=" + deviceSet.getAgentPassword() + "&localPort=" + deviceSet.getLocalPort() + "&serverID=" + deviceSet.getGbServerId() + "&channel=" + deviceSet.getChannel();
        LiveTypeDTO liveParam=new LiveTypeDTO();
        liveParam.setUrl(url);
        liveParam.setUrlType(3);
        liveParam.setVideoId(vedioId);
        liveParam.setVideoId(getVedioId(getLiveCapacity(workspaceId, deviceSn), deviceSn,deviceName));
        liveParam.setVideoQuality(0);
        ResponseResult responseResult = this.checkBeforeLive(liveParam.getVideoId());
        if (ResponseResult.CODE_SUCCESS != responseResult.getCode()) {
            return responseResult;
        System.out.println(liveStart(liveParam));
        return liveStart(liveParam);
        }
        DeviceDTO data = (DeviceDTO)responseResult.getData();
        String respTopic = THING_MODEL_PRE + PRODUCT +
                data.getDeviceSn() + SERVICES_SUF;
        //获取返回结果
        ServiceReply receiveReply = this.publishLiveStart(respTopic, liveParam);
        System.out.println(receiveReply.getResult());
        LiveDTO live = new LiveDTO();
        live.setUrl(url);
        //相机已经在直播中,请勿重复开启直播
        if(receiveReply.getResult() == 513003) {
            return ResponseResult.success(live);
        }
        return ResponseResult.success(live);
    public static String getVedioId(List<CapacityDeviceDTO> data, String sn,String name) throws IOException {
        return findAndConcatenateIndexes(data, sn,name);
    }
        public static String getVedioId(String data,String sn) {
            return findDeviceBySn(data, sn);
    public static String findAndConcatenateIndexes(List<CapacityDeviceDTO> devices, String sn, String cameraName) {
        for (CapacityDeviceDTO device : devices) {
            if (device.getSn().equals(sn)) {
                StringBuilder result = new StringBuilder();
                for (CapacityCameraDTO camera : device.getCamerasList()) {
                    if (camera.getName().equals(cameraName)) {
                        result.append(device.getSn())
                                .append("/")
                                .append(camera.getIndex())
                                .append("/")
                                .append(camera.getVideosList().get(0).getIndex()) // Assuming we take the first video index
                                .append(" "); // Add a space separator or customize as needed
        }
        public static String findDeviceBySn(String dtoListString, String snToFind) {
            String regex = "CapacityDeviceDTO\\(sn=" + snToFind + ", .*? index=([\\w\\-]+).*? index=([\\w\\-]+).*?\\)";
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(dtoListString);
            if (matcher.find()) {
                String index1 = matcher.group(1);
                String index2 = matcher.group(2);
                return snToFind + "/" + index1 + "/" + index2;
            }
            return null; // 如果未找到匹配的sn,则返回null或者适当的默认值
                return result.toString().trim(); // Trim to remove trailing space
            }
        }
        return ""; // Handle case where sn or cameraName is not found
        }
    @Override
@@ -317,6 +309,7 @@
    /**
     * Check if this lens is available live.
     * 检查镜头是否可用
     *
     * @param videoId
     * @return
     */
@@ -352,6 +345,7 @@
    /**
     * When using rtsp live, the account and password are parsed from the information returned by the pilot.
     *
     * @param url
     * @param live
     */
@@ -372,6 +366,7 @@
    /**
     * When using GB28181 live, url parameters are resolved into objects.
     *
     * @param url
     * @return
     */
@@ -399,6 +394,7 @@
    /**
     * Send a message to the pilot via mqtt to start the live streaming.
     *通过mqtt向飞行员发送消息以启动直播。
     *
     * @param topic
     * @param liveParam
     * @return
@@ -417,8 +413,10 @@
        DeviceSetEntity entity=deviceSetMapper.selectOne(new LambdaQueryWrapper<DeviceSetEntity>().eq(DeviceSetEntity::getDeviceSn,dockSn));
        return entity.getWorkspaceId();
    }
    /**
     * Send a message to the pilot via mqtt to set quality.
     *
     * @param respTopic
     * @param liveParam
     * @return
@@ -438,6 +436,7 @@
    /**
     * Send a message to the pilot via mqtt to stop the live streaming.
     *
     * @param topic
     * @param videoId
     * @return
src/main/java/com/dji/sample/media/service/IFileService.java
@@ -48,7 +48,7 @@
     * @throws ImageProcessingException
     */
    void saveMarkFile(String workspaceId, FileUploadDTO file) throws IOException, FontFormatException, ImageProcessingException;
//    void updateMarkMediaFileNames(String jobId);
    void updateMarkMediaFileNames(String jobId);
    /**
     * 获取媒体文件状态
     * @param fileId
src/main/java/com/dji/sample/media/service/impl/FileServiceImpl.java
@@ -344,7 +344,7 @@
        if (file != null) {
            builder.fileName(file.getName())
                    .filePath(file.getPath())
                    .examine(1)
                    .examine(0)
                    .fingerprint(file.getFingerprint())
                    .objectKey(file.getObjectKey())
                    .subFileType(file.getSubFileType())
@@ -454,171 +454,184 @@
    }
//    public void updateMediaFileNames(String jobId) {
//        // 查询符合条件的数据
//        List<MediaFileEntity> mediaFiles = mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>()
//                        .eq(MediaFileEntity::getJobId, jobId)
////                .eq(MediaFileMarkEntity::getIsadd, 0)
//        );
//
//        // 筛选出name字段不包含'~'的数据
//        List<MediaFileEntity> filteredFiles = mediaFiles.stream()
//                .filter(file -> !file.getFileName().contains("~"))
//                .toList();
//        for (MediaFileEntity currentFile : filteredFiles) {
//            String currentName = currentFile.getFileName();
//            Map<String, Object> currentMetadata = JSON.parseObject(JSON.toJSONString(currentFile.getMetadata()), Map.class);
//            Long currentCreatedTime = (Long) currentMetadata.get("createdTime");
//            // 找到metadata中的createdTime小于当前数据的createdTime且最接近的那条数据
//            Optional<MediaFileEntity> closestFileOpt = mediaFiles.stream()
//                    .filter(file -> {
//                        Map<String, Object> metadata = JSON.parseObject(JSON.toJSONString(file.getMetadata()), Map.class);
//                        Long createdTime = (Long) metadata.get("createdTime");
//                        String filename = file.getFileName();
//                        return createdTime < currentCreatedTime && filename.contains("~");
//                    })
//                    .min((file1, file2) -> {
//                        Map<String, Object> metadata1 = JSON.parseObject(JSON.toJSONString(file1.getMetadata()), Map.class);
//                        Map<String, Object> metadata2 = JSON.parseObject(JSON.toJSONString(file2.getMetadata()), Map.class);
//                        Long time1 = (Long) metadata1.get("createdTime");
//                        Long time2 = (Long) metadata2.get("createdTime");
//                        return Long.compare(currentCreatedTime - time1, currentCreatedTime - time2);
//                    });
//            if (closestFileOpt.isEmpty()) {
//                // 找不到小于的文件,尝试找大于且最接近的文件
//                closestFileOpt = mediaFiles.stream()
//                        .filter(file -> {
//                            Map<String, Object> metadata = JSON.parseObject(JSON.toJSONString(file.getMetadata()), Map.class);
//                            Long createdTime = (Long) metadata.get("createdTime");
//                            String filename = file.getFileName();
//                            return createdTime > currentCreatedTime && filename.contains("~");
//                        })
//                        .min((file1, file2) -> {
//                            Map<String, Object> metadata1 = JSON.parseObject(JSON.toJSONString(file1.getMetadata()), Map.class);
//                            Map<String, Object> metadata2 = JSON.parseObject(JSON.toJSONString(file2.getMetadata()), Map.class);
//                            Long time1 = (Long) metadata1.get("createdTime");
//                            Long time2 = (Long) metadata2.get("createdTime");
//                            return Long.compare(time1 - currentCreatedTime, time2 - currentCreatedTime);
//                        });}
//            if (closestFileOpt.isEmpty()) {
//                throw new RuntimeException("当前图片无法绑定图斑");
//            }
//            // 提取并替换name字段
//            closestFileOpt.ifPresent(closestFile -> {
//                String closestName = closestFile.getFileName();
//                int startIndex = closestName.indexOf("V");
//                if (closestName.contains("W")) {
//                    startIndex = closestName.indexOf("W");
//                }
//                if (closestName.contains("Z")) {
//                    startIndex = closestName.indexOf("Z");
//                }
//                int endIndex = closestName.indexOf(".", startIndex);
//                String replacement = closestName.substring(startIndex, endIndex);
//                int currentStartIndex = currentName.indexOf("V");
//                if (currentName.contains("W")) {
//                    currentStartIndex = closestName.indexOf("W");
//                }
//                if (currentName.contains("Z")) {
//                    currentStartIndex = closestName.indexOf("Z");
//                }
//                int currentEndIndex = currentName.indexOf(".", currentStartIndex);
//                String newName = currentName.substring(0, currentStartIndex)
//                        + replacement
//                        + currentName.substring(currentEndIndex);
//                currentFile.setFileName(newName);
//                updateMediaById(currentFile.getId(), currentFile);
//            });
//        }
//    }
//
//    @Override
//    public void updateMarkMediaFileNames(String jobId) {
//        try {
//
//
//            // 查询符合条件的数据
//            List<MediaFileMarkEntity> mediaFiles = markMapper.selectList(new LambdaQueryWrapper<MediaFileMarkEntity>()
//                            .eq(MediaFileMarkEntity::getJobId, jobId)
////                .eq(MediaFileMarkEntity::getIsadd, 0)
//            );
//
//
//            // 筛选出name字段不包含'~'的数据
//            List<MediaFileMarkEntity> filteredFiles = mediaFiles.stream()
//                    .filter(file -> !file.getFileName().contains("~"))
//                    .toList();
//
//            for (MediaFileMarkEntity currentFile : filteredFiles) {
//                String currentName = currentFile.getFileName();
//                Map<String, Object> currentMetadata = JSON.parseObject(JSON.toJSONString(currentFile.getMetadata()), Map.class);
//                Long currentCreatedTime = (Long) currentMetadata.get("createdTime");
//                // 找到metadata中的createdTime小于当前数据的createdTime且最接近的那条数据
//                Optional<MediaFileMarkEntity> closestFileOpt = mediaFiles.stream()
//                        .filter(file -> {
//                            Map<String, Object> metadata = JSON.parseObject(JSON.toJSONString(file.getMetadata()), Map.class);
//                            Long createdTime = (Long) metadata.get("createdTime");
//                            String filename = file.getFileName();
//                            return createdTime < currentCreatedTime && filename.contains("~");
//                        })
//                        .min((file1, file2) -> {
//                            Map<String, Object> metadata1 = JSON.parseObject(JSON.toJSONString(file1.getMetadata()), Map.class);
//                            Map<String, Object> metadata2 = JSON.parseObject(JSON.toJSONString(file2.getMetadata()), Map.class);
//                            Long time1 = (Long) metadata1.get("createdTime");
//                            Long time2 = (Long) metadata2.get("createdTime");
//                            return Long.compare(currentCreatedTime - time1, currentCreatedTime - time2);
//                        });
//                if (closestFileOpt.isEmpty()) {
//                    // 找不到小于的文件,尝试找大于且最接近的文件
//                    closestFileOpt = mediaFiles.stream()
//                            .filter(file -> {
//                                Map<String, Object> metadata = JSON.parseObject(JSON.toJSONString(file.getMetadata()), Map.class);
//                                Long createdTime = (Long) metadata.get("createdTime");
//                                String filename = file.getFileName();
//                                return createdTime > currentCreatedTime && filename.contains("~");
//                            })
//                            .min((file1, file2) -> {
//                                Map<String, Object> metadata1 = JSON.parseObject(JSON.toJSONString(file1.getMetadata()), Map.class);
//                                Map<String, Object> metadata2 = JSON.parseObject(JSON.toJSONString(file2.getMetadata()), Map.class);
//                                Long time1 = (Long) metadata1.get("createdTime");
//                                Long time2 = (Long) metadata2.get("createdTime");
//                                return Long.compare(time1 - currentCreatedTime, time2 - currentCreatedTime);
//                            });
//                }
//                if (closestFileOpt.isEmpty()) {
//                    throw new RuntimeException("没有符合时间的对象");
//                }
//                // 提取并替换name字段
//                closestFileOpt.ifPresent(closestFile -> {
//                    String closestName = closestFile.getFileName();
//                    int startIndex = closestName.indexOf("V");
//                    if (closestName.contains("W")) {
//                        startIndex = closestName.indexOf("W");
//                    }
//                    if (closestName.contains("Z")) {
//                        startIndex = closestName.indexOf("Z");
//                    }
//                    int endIndex = closestName.indexOf(".", startIndex);
//                    String replacement = closestName.substring(startIndex, endIndex);
//                    int currentStartIndex = currentName.indexOf("V");
//                    if (currentName.contains("W")) {
//                        currentStartIndex = closestName.indexOf("W");
//                    }
//                    if (currentName.contains("Z")) {
//                        currentStartIndex = closestName.indexOf("Z");
//                    }
//                    int currentEndIndex = currentName.indexOf(".", currentStartIndex);
//                    String newName = currentName.substring(0, currentStartIndex)
//                            + replacement
//                            + currentName.substring(currentEndIndex);
//                    currentFile.setFileName(newName);
//                    updateById(currentFile.getId(), currentFile);
//                });
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
    public void updateMediaFileNames(String jobId) {
        // 查询符合条件的数据
        List<MediaFileEntity> mediaFiles = mapper.selectList(new LambdaQueryWrapper<MediaFileEntity>()
                        .eq(MediaFileEntity::getJobId, jobId)
//                .eq(MediaFileMarkEntity::getIsadd, 0)
        );
        boolean allContainTilde = mediaFiles.stream().allMatch(file -> file.getFileName().contains("~"));
        boolean noneContainTilde = mediaFiles.stream().noneMatch(file -> file.getFileName().contains("~"));
        // 如果所有 fileName 都包含 '~' 或者都不包含 '~',将 is_add 字段改为 1
        if (allContainTilde || noneContainTilde) {
            return;
        }
        // 筛选出name字段不包含'~'的数据
        List<MediaFileEntity> filteredFiles = mediaFiles.stream()
                .filter(file -> !file.getFileName().contains("~"))
                .collect(Collectors.toList());
        for (MediaFileEntity currentFile : filteredFiles) {
            String currentName = currentFile.getFileName();
            Map<String, Object> currentMetadata = JSON.parseObject(JSON.toJSONString(currentFile.getMetadata()), Map.class);
            Long currentCreatedTime = (Long) currentMetadata.get("createdTime");
            // 找到metadata中的createdTime小于当前数据的createdTime且最接近的那条数据
            Optional<MediaFileEntity> closestFileOpt = mediaFiles.stream()
                    .filter(file -> {
                        Map<String, Object> metadata = JSON.parseObject(JSON.toJSONString(file.getMetadata()), Map.class);
                        Long createdTime = (Long) metadata.get("createdTime");
                        String filename = file.getFileName();
                        return createdTime < currentCreatedTime && filename.contains("~");
                    })
                    .min((file1, file2) -> {
                        Map<String, Object> metadata1 = JSON.parseObject(JSON.toJSONString(file1.getMetadata()), Map.class);
                        Map<String, Object> metadata2 = JSON.parseObject(JSON.toJSONString(file2.getMetadata()), Map.class);
                        Long time1 = (Long) metadata1.get("createdTime");
                        Long time2 = (Long) metadata2.get("createdTime");
                        return Long.compare(currentCreatedTime - time1, currentCreatedTime - time2);
                    });
            if (closestFileOpt.isEmpty()) {
                // 找不到小于的文件,尝试找大于且最接近的文件
                closestFileOpt = mediaFiles.stream()
                        .filter(file -> {
                            Map<String, Object> metadata = JSON.parseObject(JSON.toJSONString(file.getMetadata()), Map.class);
                            Long createdTime = (Long) metadata.get("createdTime");
                            String filename = file.getFileName();
                            return createdTime > currentCreatedTime && filename.contains("~");
                        })
                        .min((file1, file2) -> {
                            Map<String, Object> metadata1 = JSON.parseObject(JSON.toJSONString(file1.getMetadata()), Map.class);
                            Map<String, Object> metadata2 = JSON.parseObject(JSON.toJSONString(file2.getMetadata()), Map.class);
                            Long time1 = (Long) metadata1.get("createdTime");
                            Long time2 = (Long) metadata2.get("createdTime");
                            return Long.compare(time1 - currentCreatedTime, time2 - currentCreatedTime);
                        });
            }
            // 提取并替换name字段
            closestFileOpt.ifPresent(closestFile -> {
                String closestName = closestFile.getFileName();
                int startIndex = closestName.indexOf("V");
                if (closestName.contains("W")) {
                    startIndex = closestName.indexOf("W");
                }
                if (closestName.contains("Z")) {
                    startIndex = closestName.indexOf("Z");
                }
                int endIndex = closestName.indexOf(".", startIndex);
                String replacement = closestName.substring(startIndex, endIndex);
                int currentStartIndex = currentName.indexOf("V");
                if (currentName.contains("W")) {
                    currentStartIndex = closestName.indexOf("W");
                }
                if (currentName.contains("Z")) {
                    currentStartIndex = closestName.indexOf("Z");
                }
                int currentEndIndex = currentName.indexOf(".", currentStartIndex);
                String newName = currentName.substring(0, currentStartIndex)
                        + replacement
                        + currentName.substring(currentEndIndex);
                currentFile.setFileName(newName);
                updateMediaById(currentFile.getId(), currentFile);
            });
        }
    }
    @Override
    public void updateMarkMediaFileNames(String jobId) {
        try {
            // 查询符合条件的数据
            List<MediaFileMarkEntity> mediaFiles = markMapper.selectList(new LambdaQueryWrapper<MediaFileMarkEntity>()
                            .eq(MediaFileMarkEntity::getJobId, jobId)
//                .eq(MediaFileMarkEntity::getIsadd, 0)
            );
            // 判断所有 fileName 是否都包含 '~' 或者都不包含 '~'
            boolean allContainTilde = mediaFiles.stream().allMatch(file -> file.getFileName().contains("~"));
            boolean noneContainTilde = mediaFiles.stream().noneMatch(file -> file.getFileName().contains("~"));
            // 如果所有 fileName 都包含 '~' 或者都不包含 '~',将 is_add 字段改为 1
            if (allContainTilde || noneContainTilde) {
                mediaFiles.forEach(file -> {
                    file.setIsadd(1);
                    updateById(file.getId(), file);
                });
                    return;
            }
            // 筛选出name字段不包含'~'的数据
            List<MediaFileMarkEntity> filteredFiles = mediaFiles.stream()
                    .filter(file -> !file.getFileName().contains("~"))
                    .collect(Collectors.toList());
            for (MediaFileMarkEntity currentFile : filteredFiles) {
                String currentName = currentFile.getFileName();
                Map<String, Object> currentMetadata = JSON.parseObject(JSON.toJSONString(currentFile.getMetadata()), Map.class);
                Long currentCreatedTime = (Long) currentMetadata.get("createdTime");
                // 找到metadata中的createdTime小于当前数据的createdTime且最接近的那条数据
                Optional<MediaFileMarkEntity> closestFileOpt = mediaFiles.stream()
                        .filter(file -> {
                            Map<String, Object> metadata = JSON.parseObject(JSON.toJSONString(file.getMetadata()), Map.class);
                            Long createdTime = (Long) metadata.get("createdTime");
                            String filename = file.getFileName();
                            return createdTime < currentCreatedTime && filename.contains("~");
                        })
                        .min((file1, file2) -> {
                            Map<String, Object> metadata1 = JSON.parseObject(JSON.toJSONString(file1.getMetadata()), Map.class);
                            Map<String, Object> metadata2 = JSON.parseObject(JSON.toJSONString(file2.getMetadata()), Map.class);
                            Long time1 = (Long) metadata1.get("createdTime");
                            Long time2 = (Long) metadata2.get("createdTime");
                            return Long.compare(currentCreatedTime - time1, currentCreatedTime - time2);
                        });
                if (closestFileOpt.isEmpty()) {
                    // 找不到小于的文件,尝试找大于且最接近的文件
                    closestFileOpt = mediaFiles.stream()
                            .filter(file -> {
                                Map<String, Object> metadata = JSON.parseObject(JSON.toJSONString(file.getMetadata()), Map.class);
                                Long createdTime = (Long) metadata.get("createdTime");
                                String filename = file.getFileName();
                                return createdTime > currentCreatedTime && filename.contains("~");
                            })
                            .min((file1, file2) -> {
                                Map<String, Object> metadata1 = JSON.parseObject(JSON.toJSONString(file1.getMetadata()), Map.class);
                                Map<String, Object> metadata2 = JSON.parseObject(JSON.toJSONString(file2.getMetadata()), Map.class);
                                Long time1 = (Long) metadata1.get("createdTime");
                                Long time2 = (Long) metadata2.get("createdTime");
                                return Long.compare(time1 - currentCreatedTime, time2 - currentCreatedTime);
                            });
                }
                // 提取并替换name字段
                closestFileOpt.ifPresent(closestFile -> {
                    String closestName = closestFile.getFileName();
                    int startIndex = closestName.indexOf("V");
                    if (closestName.contains("W")) {
                        startIndex = closestName.indexOf("W");
                    }
                    if (closestName.contains("Z")) {
                        startIndex = closestName.indexOf("Z");
                    }
                    int endIndex = closestName.indexOf(".", startIndex);
                    String replacement = closestName.substring(startIndex, endIndex);
                    int currentStartIndex = currentName.indexOf("V");
                    if (currentName.contains("W")) {
                        currentStartIndex = closestName.indexOf("W");
                    }
                    if (currentName.contains("Z")) {
                        currentStartIndex = closestName.indexOf("Z");
                    }
                    int currentEndIndex = currentName.indexOf(".", currentStartIndex);
                    String newName = currentName.substring(0, currentStartIndex)
                            + replacement
                            + currentName.substring(currentEndIndex);
                    currentFile.setFileName(newName);
                    updateById(currentFile.getId(), currentFile);
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void updateById(Integer id, MediaFileMarkEntity entity) {
        entity.setIsadd(1);
@@ -633,13 +646,13 @@
        mapper.update(entity, updateWrapper);
    }
//    public void getNoaddFile() {
//        List<MediaFileMarkEntity> markEntities = markMapper.selectList(new LambdaQueryWrapper<MediaFileMarkEntity>().eq(MediaFileMarkEntity::getIsadd, 0));
//        for (MediaFileMarkEntity mark : markEntities) {
//            updateMarkMediaFileNames(mark.getJobId());
//            updateMediaFileNames(mark.getJobId());
//        }
//    }
    public void getNoaddFile() {
        List<MediaFileMarkEntity> markEntities = markMapper.selectList(new LambdaQueryWrapper<MediaFileMarkEntity>().eq(MediaFileMarkEntity::getIsadd, 0));
        for (MediaFileMarkEntity mark : markEntities) {
            updateMarkMediaFileNames(mark.getJobId());
            updateMediaFileNames(mark.getJobId());
        }
    }
}
src/main/java/com/dji/sample/patches/controller/PatchesController.java
@@ -202,8 +202,9 @@
                                                                           @RequestParam(name = "page_size", defaultValue = "10") Integer pageSize,
                                                                           @RequestParam String workspaceId,
                                                                           @RequestParam String dkbh) {
//        fileService.getNoaddFile();
        try {
            fileService.getNoaddFile();
            PatchesParam param = PatchesParam.builder()
                    .page(page)
                    .workspaceId(workspaceId)