zhaowenyu
2026-03-07 bc8f980e912825eaaadaf9fdd2367f443a56de02
2026-03-07 标记功能接口,支持点、线、面、圆标记存储
14 files added
734 ■■■■■ changed files
ard-admin/src/main/java/com/ard/web/controller/sample/AnnotationController.java 197 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/mapper/AnnotationDirectoryMapper.java 9 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/mapper/AnnotationMapper.java 14 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDTO.java 37 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDirectoryDTO.java 31 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationPointsDTO.java 26 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/model/entity/AnnotationDirectoryEntity.java 33 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/model/entity/AnnotationEntity.java 50 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/service/IAnnotationDirectoryService.java 19 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/service/IAnnotationService.java 18 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/service/impl/AnnotationDirectoryServiceImpl.java 147 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/service/impl/AnnotationServiceImpl.java 64 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/util/ConvertUtil.java 57 ●●●●● patch | view | raw | blame | history
ard-sample/src/main/java/com/ard/sample/annotation/util/JsonSerializeUtil.java 32 ●●●●● patch | view | raw | blame | history
ard-admin/src/main/java/com/ard/web/controller/sample/AnnotationController.java
New file
@@ -0,0 +1,197 @@
package com.ard.web.controller.sample;
import com.ard.sample.annotation.model.dto.AnnotationDTO;
import com.ard.sample.annotation.model.dto.AnnotationDirectoryDTO;
import com.ard.sample.annotation.model.entity.AnnotationDirectoryEntity;
import com.ard.sample.annotation.model.entity.AnnotationEntity;
import com.ard.sample.annotation.service.IAnnotationDirectoryService;
import com.ard.sample.annotation.service.IAnnotationService;
import com.ard.sample.annotation.util.ConvertUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Api(tags = "标注管理")
@Slf4j
@RestController
@RequestMapping("/annotation")
@RequiredArgsConstructor
public class AnnotationController {
    private final IAnnotationDirectoryService directoryService;
    private final IAnnotationService annotationService;
    // ====================== 目录接口 ======================
    @ApiOperation("新增目录")
    @PostMapping("/directory")
    public ResponseEntity<Map<String, Object>> addDirectory(@RequestBody AnnotationDirectoryEntity entity) {
        Map<String, Object> result = new HashMap<>();
        try {
            //AnnotationDirectoryEntity entity = ConvertUtil.directoryDtoToEntity(dto);
            boolean success = directoryService.addDirectory(entity);
            result.put("success", success);
            result.put("msg", success ? "目录新增成功" : "目录新增失败");
            result.put("data", entity.getId()); // 返回新增ID
        } catch (Exception e) {
            log.error("新增目录异常", e);
            result.put("success", false);
            result.put("msg", "新增目录失败:" + e.getMessage());
        }
        return ResponseEntity.ok(result);
    }
    @ApiOperation("修改目录")
    @PutMapping("/directory")
    public ResponseEntity<Map<String, Object>> editDirectory(@RequestBody AnnotationDirectoryEntity entity) {
        Map<String, Object> result = new HashMap<>();
        try {
//            AnnotationDirectoryEntity entity = ConvertUtil.directoryDtoToEntity(dto);
            boolean success = directoryService.editDirectory(entity);
            result.put("success", success);
            result.put("msg", success ? "目录修改成功" : "目录修改失败");
        } catch (Exception e) {
            log.error("修改目录异常", e);
            result.put("success", false);
            result.put("msg", "修改目录失败:" + e.getMessage());
        }
        return ResponseEntity.ok(result);
    }
    @ApiOperation("删除目录及子项")
    @DeleteMapping("/directory/{id}")
    public ResponseEntity<Map<String, Object>> deleteDirectory(@PathVariable Integer id) {
        Map<String, Object> result = new HashMap<>();
        try {
            boolean success = directoryService.deleteDirectory(id);
            result.put("success", success);
            result.put("msg", success ? "目录删除成功" : "目录删除失败");
        } catch (Exception e) {
            log.error("删除目录异常", e);
            result.put("success", false);
            result.put("msg", "删除目录失败:" + e.getMessage());
        }
        return ResponseEntity.ok(result);
    }
    // ====================== 标记接口 ======================
    @ApiOperation("新增标记")
    @PostMapping
    public ResponseEntity<Map<String, Object>> addAnnotation(@RequestBody AnnotationDTO dto) {
        Map<String, Object> result = new HashMap<>();
        try {
            AnnotationEntity entity = ConvertUtil.annotationDtoToEntity(dto);
            boolean success = annotationService.addAnnotation(entity);
            result.put("success", success);
            result.put("msg", success ? "标记新增成功" : "标记新增失败");
            result.put("data", entity.getId());
        } catch (Exception e) {
            log.error("新增标记异常", e);
            result.put("success", false);
            result.put("msg", "新增标记失败:" + e.getMessage());
        }
        return ResponseEntity.ok(result);
    }
    @ApiOperation("修改标记")
    @PutMapping
    public ResponseEntity<Map<String, Object>> editAnnotation(@RequestBody AnnotationDTO dto) {
        Map<String, Object> result = new HashMap<>();
        try {
            AnnotationEntity entity = ConvertUtil.annotationDtoToEntity(dto);
            boolean success = annotationService.editAnnotation(entity);
            result.put("success", success);
            result.put("msg", success ? "标记修改成功" : "标记修改失败");
        } catch (Exception e) {
            log.error("修改标记异常", e);
            result.put("success", false);
            result.put("msg", "修改标记失败:" + e.getMessage());
        }
        return ResponseEntity.ok(result);
    }
    @ApiOperation("删除标记")
    @DeleteMapping("/{id}")
    public ResponseEntity<Map<String, Object>> deleteAnnotation(@PathVariable Integer id) {
        Map<String, Object> result = new HashMap<>();
        try {
            boolean success = annotationService.deleteAnnotation(id);
            result.put("success", success);
            result.put("msg", success ? "标记删除成功" : "标记删除失败");
        } catch (Exception e) {
            log.error("删除标记异常", e);
            result.put("success", false);
            result.put("msg", "删除标记失败:" + e.getMessage());
        }
        return ResponseEntity.ok(result);
    }
    @ApiOperation("批量删除标记")
    @DeleteMapping("/batch")
    public ResponseEntity<Map<String, Object>> batchDeleteAnnotation(@RequestBody List<Integer> ids) {
        Map<String, Object> result = new HashMap<>();
        try {
            boolean success = annotationService.batchDeleteAnnotation(ids);
            result.put("success", success);
            result.put("msg", success ? "标记批量删除成功" : "标记批量删除失败");
        } catch (Exception e) {
            log.error("批量删除标记异常", e);
            result.put("success", false);
            result.put("msg", "批量删除标记失败:" + e.getMessage());
        }
        return ResponseEntity.ok(result);
    }
    // 层级查询:目录→标记→坐标
    @ApiOperation("按目标id获取所有目录及子标记信息")
    @GetMapping("/directory/{directoryId}")
    public ResponseEntity<Map<String, Object>> getDirectoryWithAnnotations(@PathVariable Integer directoryId) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 1. 查询目录
            AnnotationDirectoryEntity dirEntity = directoryService.getById(directoryId);
            if (dirEntity == null) {
                result.put("success", false);
                result.put("msg", "目录不存在");
                return ResponseEntity.ok(result);
            }
            AnnotationDirectoryDTO dirDto = ConvertUtil.directoryEntityToDto(dirEntity);
            // 2. 查询目录下标记
            List<AnnotationEntity> annoEntities = annotationService.listByDirectoryId(directoryId);
            List<AnnotationDTO> annoDtos = ConvertUtil.annotationEntityListToDtoList(annoEntities);
            dirDto.setAnnotations(annoDtos);
            result.put("success", true);
            result.put("data", dirDto);
        } catch (Exception e) {
            log.error("查询目录及标记异常", e);
            result.put("success", false);
            result.put("msg", "查询失败:" + e.getMessage());
        }
        return ResponseEntity.ok(result);
    }
    /**
     * 新增:获取全部目录及下属标注数据
     */
    @ApiOperation("获取全部目录及下属标注数据")
    @GetMapping("/directory/all")
    public ResponseEntity<Map<String, Object>> listAllDirectoryWithAnnotations() {
        List<AnnotationDirectoryDTO> directoryList = directoryService.listAllDirectoryWithAnnotations();
        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("msg", "查询全部目录及标注成功");
        result.put("data", directoryList);
        return ResponseEntity.ok(result);
    }
}
ard-sample/src/main/java/com/ard/sample/annotation/mapper/AnnotationDirectoryMapper.java
New file
@@ -0,0 +1,9 @@
package com.ard.sample.annotation.mapper;
import com.ard.sample.annotation.model.entity.AnnotationDirectoryEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AnnotationDirectoryMapper extends BaseMapper<AnnotationDirectoryEntity> {
}
ard-sample/src/main/java/com/ard/sample/annotation/mapper/AnnotationMapper.java
New file
@@ -0,0 +1,14 @@
package com.ard.sample.annotation.mapper;
import com.ard.sample.annotation.model.entity.AnnotationEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface AnnotationMapper extends BaseMapper<AnnotationEntity> {
    // 按目录ID查询标记列表
    List<AnnotationEntity> selectByDirectoryId(@Param("directoryId") Integer directoryId);
}
ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDTO.java
New file
@@ -0,0 +1,37 @@
package com.ard.sample.annotation.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class AnnotationDTO {
    private Integer id;
    private Integer directoryId;
    private String name;
    private String color;
    private String annotationType;
    private List<AnnotationPointsDTO> coordinateInfo;
    private String imageUrl;
    private String drawer;
    private String description;
    private Long createTime;
    private Long updateTime;
}
ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDirectoryDTO.java
New file
@@ -0,0 +1,31 @@
package com.ard.sample.annotation.model.dto;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
 * 标注目录的DTO类
 * */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class AnnotationDirectoryDTO {
    private Integer id;
    private String directoryName;
    private List<AnnotationDTO> annotations; // 层级返回用
    private String directoryRemark;
    private Long createTime;
    private Long updateTime;
}
ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationPointsDTO.java
New file
@@ -0,0 +1,26 @@
package com.ard.sample.annotation.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 点、线、面、圆标注的DTO类
 * */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class AnnotationPointsDTO {
    private Double longitude;
    private Double latitude;
    private Double height;
    private Double radius;
}
ard-sample/src/main/java/com/ard/sample/annotation/model/entity/AnnotationDirectoryEntity.java
New file
@@ -0,0 +1,33 @@
package com.ard.sample.annotation.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "annotation_directory")
public class AnnotationDirectoryEntity implements Serializable {
    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField(value = "directory_name")
    private String directoryName;
    @TableField(value = "directory_remark")
    private String directoryRemark;
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private Long createTime;
    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    private Long updateTime;
}
ard-sample/src/main/java/com/ard/sample/annotation/model/entity/AnnotationEntity.java
New file
@@ -0,0 +1,50 @@
package com.ard.sample.annotation.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "annotation")
public class AnnotationEntity implements Serializable {
    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField(value = "directory_id")
    private Integer directoryId;
    @TableField(value = "name")
    private String name;
    @TableField(value = "color")
    private String color;
    @TableField(value = "annotation_type")
    private String annotationType;
    @TableField(value = "coordinate_info")
    private byte[] coordinateInfo;
    @TableField(value = "image_url")
    private String imageUrl;
    @TableField(value = "drawer")
    private String drawer;
    @TableField(value = "description")
    private String description;
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private Long createTime;
    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    private Long updateTime;
}
ard-sample/src/main/java/com/ard/sample/annotation/service/IAnnotationDirectoryService.java
New file
@@ -0,0 +1,19 @@
package com.ard.sample.annotation.service;
import com.ard.sample.annotation.model.dto.AnnotationDirectoryDTO;
import com.ard.sample.annotation.model.entity.AnnotationDirectoryEntity;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface IAnnotationDirectoryService extends IService<AnnotationDirectoryEntity> {
    // 新增目录
    boolean addDirectory(AnnotationDirectoryEntity entity);
    // 修改目录
    boolean editDirectory(AnnotationDirectoryEntity entity);
    // 删除目录(级联删除标记)
    boolean deleteDirectory(Integer id);
    // 新增:查询全部目录(含下属标注数据)
    List<AnnotationDirectoryDTO> listAllDirectoryWithAnnotations();
}
ard-sample/src/main/java/com/ard/sample/annotation/service/IAnnotationService.java
New file
@@ -0,0 +1,18 @@
package com.ard.sample.annotation.service;
import com.ard.sample.annotation.model.entity.AnnotationEntity;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface IAnnotationService extends IService<AnnotationEntity> {
    // 新增标记
    boolean addAnnotation(AnnotationEntity entity);
    // 修改标记
    boolean editAnnotation(AnnotationEntity entity);
    // 删除标记
    boolean deleteAnnotation(Integer id);
    // 批量删除标记
    boolean batchDeleteAnnotation(List<Integer> ids);
    // 按目录ID查询标记
    List<AnnotationEntity> listByDirectoryId(Integer directoryId);
}
ard-sample/src/main/java/com/ard/sample/annotation/service/impl/AnnotationDirectoryServiceImpl.java
New file
@@ -0,0 +1,147 @@
package com.ard.sample.annotation.service.impl;
import com.ard.common.utils.bean.BeanUtils;
import com.ard.sample.annotation.mapper.AnnotationMapper;
import com.ard.sample.annotation.model.dto.AnnotationDTO;
import com.ard.sample.annotation.model.dto.AnnotationDirectoryDTO;
import com.ard.sample.annotation.model.entity.AnnotationDirectoryEntity;
import com.ard.sample.annotation.model.entity.AnnotationEntity;
import com.ard.sample.annotation.service.IAnnotationDirectoryService;
import com.ard.sample.annotation.service.IAnnotationService;
import com.ard.sample.annotation.util.JsonSerializeUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ard.sample.annotation.mapper.AnnotationDirectoryMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class AnnotationDirectoryServiceImpl extends ServiceImpl<AnnotationDirectoryMapper, AnnotationDirectoryEntity>
        implements IAnnotationDirectoryService {
    // 注入已实现的AnnotationService(而非直接用Mapper),复用其查询逻辑
    private final IAnnotationService annotationService;
    private final AnnotationMapper annotationMapper;
    @Override
    public boolean addDirectory(AnnotationDirectoryEntity entity) {
        return save(entity);
    }
    @Override
    public boolean editDirectory(AnnotationDirectoryEntity entity) {
        if (entity.getId() == null) {
            log.error("修改目录失败:ID为空");
            return false;
        }
        return updateById(entity);
    }
    @Override
    @Transactional(rollbackFor = Exception.class) // 事务保证级联删除
    public boolean deleteDirectory(Integer id) {
        if (id == null) {
            log.error("删除目录失败:ID为空");
            return false;
        }
        // 1. 删除目录下所有标记
        annotationMapper.deleteByMap(Map.of("directory_id", id));
        // 2. 删除目录
        boolean result = removeById(id);
        log.info("删除目录{},结果:{}", id, result);
        return result;
    }
    /**
     * 查询全部目录(含下属标注数据)
     * 修复点:替换自定义Mapper方法为MyBatis-Plus通用查询,避免SQL映射缺失问题
     */
    @Override
    public List<AnnotationDirectoryDTO> listAllDirectoryWithAnnotations() {
        // 1. 查询所有目录
        List<AnnotationDirectoryEntity> directoryEntities = list();
        if (directoryEntities.isEmpty()) {
            return List.of();
        }
        // 2. 批量查询所有标注,按目录ID分组(减少数据库查询次数)
        List<AnnotationEntity> allAnnotations = annotationService.list();
        Map<Integer, List<AnnotationEntity>> annotationMap = allAnnotations.stream()
                .collect(Collectors.groupingBy(AnnotationEntity::getDirectoryId));
        // 3. 遍历目录,组装DTO(关联下属标注)
        return directoryEntities.stream().map(directoryEntity -> {
            AnnotationDirectoryDTO directoryDTO = AnnotationDirectoryDTO.builder().build();
            BeanUtils.copyProperties(directoryEntity, directoryDTO);
            // 4. 获取当前目录下的标注(从分组Map中获取,避免循环查库)
            List<AnnotationEntity> annotationEntities = annotationMap.getOrDefault(directoryEntity.getId(), List.of());
            // 5. 标注实体转DTO(反序列化坐标数据)
            List<AnnotationDTO> annotationDTOList = annotationEntities.stream().map(annotationEntity -> {
                AnnotationDTO annotationDTO = AnnotationDTO.builder().build();
                BeanUtils.copyProperties(annotationEntity, annotationDTO);
                try {
                    // 反序列化坐标数据
                    annotationDTO.setCoordinateInfo(
                            JsonSerializeUtil.deserialize(annotationEntity.getCoordinateInfo())
                    );
                } catch (Exception e) {
                    log.error("反序列化标注坐标失败,标注ID:{}", annotationEntity.getId(), e);
                    annotationDTO.setCoordinateInfo(null);
                }
                return annotationDTO;
            }).collect(Collectors.toList());
            // 6. 设置目录下的标注列表
            directoryDTO.setAnnotations(annotationDTOList);
            return directoryDTO;
        }).collect(Collectors.toList());
    }
//    /**
//     * 查询全部目录(含下属标注数据)
//     */
//    @Override
//    public List<AnnotationDirectoryDTO> listAllDirectoryWithAnnotations() {
//        // 1. 查询所有目录
//        List<AnnotationDirectoryEntity> directoryEntities = list();
//
//        // 2. 遍历目录,关联查询标注数据并组装DTO
//        return directoryEntities.stream().map(directoryEntity -> {
//            AnnotationDirectoryDTO directoryDTO = AnnotationDirectoryDTO.builder().build();
//            BeanUtils.copyProperties(directoryEntity, directoryDTO);
//
//            // 3. 查询当前目录下的所有标注
//            List<AnnotationEntity> annotationEntities = annotationMapper.selectByDirectoryId(directoryEntity.getId());
//
//            // 4. 标注实体转DTO(反序列化坐标数据)
//            List<AnnotationDTO> annotationDTOList = annotationEntities.stream().map(annotationEntity -> {
//                AnnotationDTO annotationDTO = AnnotationDTO.builder().build();
//                BeanUtils.copyProperties(annotationEntity, annotationDTO);
//                try {
//                    // 反序列化坐标数据
//                    annotationDTO.setCoordinateInfo(
//                            JsonSerializeUtil.deserialize(annotationEntity.getCoordinateInfo())
//                    );
//                } catch (Exception e) {
//                    log.error("反序列化标注坐标失败,标注ID:{}", annotationEntity.getId(), e);
//                    annotationDTO.setCoordinateInfo(null);
//                }
//                return annotationDTO;
//            }).collect(Collectors.toList());
//
//            // 5. 给目录DTO设置下属标注列表(需先给AnnotationDirectoryDTO新增annotations字段)
//            directoryDTO.setAnnotations(annotationDTOList);
//            return directoryDTO;
//        }).collect(Collectors.toList());
//    }
}
ard-sample/src/main/java/com/ard/sample/annotation/service/impl/AnnotationServiceImpl.java
New file
@@ -0,0 +1,64 @@
package com.ard.sample.annotation.service.impl;
import com.ard.sample.annotation.mapper.AnnotationMapper;
import com.ard.sample.annotation.model.entity.AnnotationEntity;
import com.ard.sample.annotation.service.IAnnotationService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class AnnotationServiceImpl extends ServiceImpl<AnnotationMapper, AnnotationEntity>
        implements IAnnotationService {
    @Override
    public boolean addAnnotation(AnnotationEntity entity) {
        return save(entity);
    }
    @Override
    public boolean editAnnotation(AnnotationEntity entity) {
        if (entity.getId() == null) {
            log.error("修改标记失败:ID为空");
            return false;
        }
        return updateById(entity);
    }
    @Override
    public boolean deleteAnnotation(Integer id) {
        if (id == null) {
            log.error("删除标记失败:ID为空");
            return false;
        }
        boolean result = removeById(id);
        log.info("删除标记{},结果:{}", id, result);
        return result;
    }
    @Override
    public boolean batchDeleteAnnotation(List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            log.error("批量删除标记失败:ID列表为空");
            return false;
        }
        boolean result = removeByIds(ids);
        log.info("批量删除标记{},结果:{}", ids, result);
        return result;
    }
    @Override
    public List<AnnotationEntity> listByDirectoryId(Integer directoryId) {
        if (directoryId == null) {
            log.error("按目录查询标记失败:目录ID为空");
            return List.of();
        }
        return lambdaQuery().eq(AnnotationEntity::getDirectoryId, directoryId).list();
    }
}
ard-sample/src/main/java/com/ard/sample/annotation/util/ConvertUtil.java
New file
@@ -0,0 +1,57 @@
package com.ard.sample.annotation.util;
import com.ard.sample.annotation.model.dto.AnnotationDTO;
import com.ard.sample.annotation.model.dto.AnnotationDirectoryDTO;
import com.ard.sample.annotation.model.dto.AnnotationPointsDTO;
import com.ard.sample.annotation.model.entity.AnnotationDirectoryEntity;
import com.ard.sample.annotation.model.entity.AnnotationEntity;
import org.springframework.beans.BeanUtils;
import java.util.List;
import java.util.stream.Collectors;
public class ConvertUtil {
    // 目录DTO → 目录Entity
    public static AnnotationDirectoryEntity directoryDtoToEntity(AnnotationDirectoryDTO dto) {
        AnnotationDirectoryEntity entity = new AnnotationDirectoryEntity();
        BeanUtils.copyProperties(dto, entity);
        return entity;
    }
    // 目录Entity → 目录DTO
    public static AnnotationDirectoryDTO directoryEntityToDto(AnnotationDirectoryEntity entity) {
        AnnotationDirectoryDTO dto = new AnnotationDirectoryDTO();
        BeanUtils.copyProperties(entity, dto);
        return dto;
    }
    // 标记DTO → 标记Entity(含坐标序列化)
    public static AnnotationEntity annotationDtoToEntity(AnnotationDTO dto) throws Exception {
        AnnotationEntity entity = new AnnotationEntity();
        BeanUtils.copyProperties(dto, entity);
        // 序列化坐标数据
        entity.setCoordinateInfo(JsonSerializeUtil.serialize(dto.getCoordinateInfo()));
        return entity;
    }
    // 标记Entity → 标记DTO(含坐标反序列化)
    public static AnnotationDTO annotationEntityToDto(AnnotationEntity entity) throws Exception {
        AnnotationDTO dto = new AnnotationDTO();
        BeanUtils.copyProperties(entity, dto);
        // 反序列化坐标数据
        List<AnnotationPointsDTO> points = JsonSerializeUtil.deserialize(entity.getCoordinateInfo());
        dto.setCoordinateInfo(points);
        return dto;
    }
    // 标记Entity列表 → 标记DTO列表
    public static List<AnnotationDTO> annotationEntityListToDtoList(List<AnnotationEntity> entityList) {
        return entityList.stream().map(entity -> {
            try {
                return annotationEntityToDto(entity);
            } catch (Exception e) {
                throw new RuntimeException("标记Entity转DTO失败", e);
            }
        }).collect(Collectors.toList());
    }
}
ard-sample/src/main/java/com/ard/sample/annotation/util/JsonSerializeUtil.java
New file
@@ -0,0 +1,32 @@
package com.ard.sample.annotation.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ard.sample.annotation.model.dto.AnnotationPointsDTO;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class JsonSerializeUtil {
    private static final ObjectMapper objectMapper = new ObjectMapper();
    // 序列化:List<AnnotationPointsDTO> → byte[]
    public static byte[] serialize(List<AnnotationPointsDTO> points) throws JsonProcessingException {
        if (points == null) {
            return null;
        }
        String json = objectMapper.writeValueAsString(points);
        return json.getBytes();
    }
    // 反序列化:byte[] → List<AnnotationPointsDTO>
    public static List<AnnotationPointsDTO> deserialize(byte[] bytes) throws JsonProcessingException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        String json = new String(bytes);
        return objectMapper.readValue(json,
                objectMapper.getTypeFactory().constructCollectionType(List.class, AnnotationPointsDTO.class));
    }
}