| | |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.model.operation.XpElectronicFence; |
| | | import com.xiaopeng.api.model.operation.XpOpenCity; |
| | | import com.google.common.collect.ImmutableMap; |
| | | import com.xiaopeng.api.dto.operation.ElectronicFenceAddDto; |
| | | import com.xiaopeng.api.dto.operation.ElectronicFenceUpdateDto; |
| | | import com.xiaopeng.api.model.map.XpGeoFencingPolygon; |
| | | import com.xiaopeng.api.model.map.XpGeoFencingProhibitedPolygon; |
| | | import com.xiaopeng.api.model.operation.*; |
| | | import com.xiaopeng.api.query.operation.ElectronicPageListQuery; |
| | | import com.xiaopeng.api.vo.operation.XpElectronicFenceVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.exception.ServiceException; |
| | | import com.xiaopeng.common.core.utils.DateUtils; |
| | | import com.xiaopeng.common.core.utils.StringUtils; |
| | | import com.xiaopeng.common.core.utils.bean.BeanUtils; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import com.xiaopeng.map.constant.I18nKeyConstant; |
| | | import com.xiaopeng.map.job.base.QuartzManager; |
| | | import com.xiaopeng.map.job.base.TimeJobType; |
| | | import com.xiaopeng.map.job.jobs.ElectronicEffectJob; |
| | | import com.xiaopeng.map.mapper.XpElectronicFenceAreaMapper; |
| | | import com.xiaopeng.map.mapper.XpElectronicFenceMapper; |
| | | import com.xiaopeng.map.mapper.XpPointMapper; |
| | | import com.xiaopeng.map.mapper.XpRegionMapper; |
| | | import com.xiaopeng.map.service.XpElectronicFenceService; |
| | | import com.xiaopeng.map.utils.PolygonOverlapService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import net.sourceforge.pinyin4j.PinyinHelper; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.data.geo.Point; |
| | | import org.springframework.data.mongodb.core.MongoTemplate; |
| | | import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; |
| | | import org.springframework.data.mongodb.core.query.Criteria; |
| | | import org.springframework.data.mongodb.core.query.Query; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | * @since 2026-02-03 |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | public class XpElectronicFenceServiceImpl extends ServiceImpl<XpElectronicFenceMapper, XpElectronicFence> implements XpElectronicFenceService { |
| | | |
| | | @Resource |
| | | private PolygonOverlapService polygonOverlapService; |
| | | @Resource |
| | | private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper; |
| | | @Autowired |
| | | private MongoTemplate mongoTemplate; |
| | | @Autowired |
| | | private XpPointMapper xpPointMapper; |
| | | @Autowired |
| | | private XpRegionMapper xpRegionMapper; |
| | | |
| | | @Override |
| | | public PageInfo<XpElectronicFence> pageList(ElectronicPageListQuery query) { |
| | | PageInfo<XpElectronicFence> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); |
| | | if (StringUtils.isEmpty(query.getCreateSort())&& StringUtils.isEmpty(query.getUpdateSort())){ |
| | | query.setCreateSort("2"); |
| | | } |
| | | List<XpElectronicFence> list = this.baseMapper.pageList(pageInfo, query); |
| | | if (list.isEmpty()) { |
| | | return new PageInfo<>(); |
| | | } |
| | | List<Long> electronicIds = list.stream().map(XpElectronicFence::getId).collect(Collectors.toList()); |
| | | Map<Long, List<XpElectronicFenceArea>> electronicFenceAreaMap = xpElectronicFenceAreaMapper.selectList(new LambdaQueryWrapper<XpElectronicFenceArea>() |
| | | .in(XpElectronicFenceArea::getElectronicId, electronicIds)).stream() |
| | | .collect(Collectors.groupingBy(XpElectronicFenceArea::getElectronicId)); |
| | | for (XpElectronicFence xpElectronicFence : list) { |
| | | List<XpElectronicFenceArea> xpElectronicFenceAreas = electronicFenceAreaMap.get(xpElectronicFence.getId()); |
| | | if (xpElectronicFenceAreas != null) { |
| | | xpElectronicFence.setOperatingAreaCount(xpElectronicFenceAreas.stream().filter(xpElectronicFenceArea -> xpElectronicFenceArea.getAreaType() == 1).count()); |
| | | xpElectronicFence.setProhibitedAreaCount(xpElectronicFenceAreas.stream().filter(xpElectronicFenceArea -> xpElectronicFenceArea.getAreaType() == 2).count()); |
| | | }else{ |
| | | xpElectronicFence.setOperatingAreaCount(0L); |
| | | xpElectronicFence.setProhibitedAreaCount(0L); |
| | | } |
| | | } |
| | | pageInfo.setRecords(list); |
| | | return pageInfo; |
| | | } |
| | | public static String getPinyinInitials(String chinese) { |
| | | StringBuilder pinyinInitials = new StringBuilder(); |
| | | for (char c : chinese.toCharArray()) { |
| | | // 获取拼音数组(多音字会返回多个拼音) |
| | | String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c); |
| | | if (pinyinArray != null && pinyinArray.length > 0) { |
| | | // 取第一个拼音的首字母 |
| | | pinyinInitials.append(pinyinArray[0].charAt(0)); |
| | | } |
| | | } |
| | | return pinyinInitials.toString(); |
| | | } |
| | | @Override |
| | | public R addElectronic(ElectronicFenceAddDto dto) { |
| | | try { |
| | | // 获取现有的运营区域(areaType=1) |
| | | LambdaQueryWrapper<XpElectronicFenceArea> xpElectronicFenceAreaLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
| | | xpElectronicFenceAreaLambdaQueryWrapper.eq(XpElectronicFenceArea::getAreaType, 1); |
| | | List<XpElectronicFenceArea> existingOperationAreas = xpElectronicFenceAreaMapper.selectList(xpElectronicFenceAreaLambdaQueryWrapper); |
| | | |
| | | // 获取待添加的新区域 |
| | | List<XpElectronicFenceArea> newElectronicFenceAreas = dto.getElectronicFenceAreas(); |
| | | |
| | | |
| | | // 检查新区域与现有运营区域是否有重叠 |
| | | for (XpElectronicFenceArea newArea : newElectronicFenceAreas) { |
| | | if (newArea.getAreaType() == 1) { // 只检查运营区域 |
| | | for (XpElectronicFenceArea existingArea : existingOperationAreas) { |
| | | if (polygonOverlapService.checkPolygonOverlap( |
| | | newArea.getFencePoints(), |
| | | existingArea.getFencePoints())) { |
| | | throw new ServiceException( |
| | | "运营区域{}与现有运营区域{}发生重叠", |
| | | I18nKeyConstant.EDIT_ELECTRONIC_ERROR, |
| | | new Object[]{existingArea.getAreaName(), existingArea.getAreaName()} |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 保存主表数据 |
| | | XpElectronicFence xpElectronicFence = new XpElectronicFence(); |
| | | BeanUtils.copyProperties(dto, xpElectronicFence); |
| | | xpElectronicFence.setStatus(0); |
| | | |
| | | if (dto.getStatus() == 0) { |
| | | xpElectronicFence.setStatus(0); |
| | | } else if (dto.getStatus() == 1) { |
| | | if (dto.getEffectType() == 1) { |
| | | // 设置为启用中 |
| | | xpElectronicFence.setStatus(2); |
| | | xpElectronicFence.setEffectTime(LocalDateTime.now()); |
| | | } else { |
| | | if (dto.getEffectTime().isBefore(LocalDateTime.now())) { |
| | | // 设置为启用中 |
| | | xpElectronicFence.setStatus(2); |
| | | } else { |
| | | xpElectronicFence.setStatus(1); |
| | | } |
| | | } |
| | | } |
| | | xpElectronicFence.setCityCode(dto.getCityCode()); |
| | | // 生成围栏id 规则:城市首字母-类型简写-4位自增流水号(如 SZ-OP-001),支持复制;数据停用或删除后,流水号不回收 |
| | | XpRegion xpRegion = xpRegionMapper.selectOne(new LambdaQueryWrapper<XpRegion>() |
| | | .eq(XpRegion::getCode, dto.getCityCode())); |
| | | String name = xpRegion.getName(); |
| | | this.save(xpElectronicFence); |
| | | // 根据城市中文名,截取前两位的首字母并且大写 |
| | | // 截取前两个字符 |
| | | String prefix = name.substring(0, 2); |
| | | // 获取拼音首字母并大写 |
| | | String result = getPinyinInitials(prefix).toUpperCase(); |
| | | xpElectronicFence.setElectronicNumber(result + "-OP-" + String.format("%04d", xpElectronicFence.getId())); |
| | | if (dto.getStatus() == 0) { |
| | | xpElectronicFence.setStatus(0); |
| | | } else if (dto.getStatus() == 1) { |
| | | if (dto.getEffectType() == 1) { |
| | | // 设置为启用中 |
| | | xpElectronicFence.setStatus(2); |
| | | xpElectronicFence.setEffectTime(LocalDateTime.now()); |
| | | } else { |
| | | if (dto.getEffectTime().isBefore(LocalDateTime.now())) { |
| | | // 设置为启用中 |
| | | xpElectronicFence.setStatus(2); |
| | | } else { |
| | | // 添加定时任务job type=1电子围栏生效 type=2电子围栏失效 |
| | | Map<String, ? extends Object> maps = |
| | | new ImmutableMap.Builder<String, Long>(). |
| | | put("type", 1L) |
| | | .put("id", xpElectronicFence.getId()) |
| | | .build(); |
| | | QuartzManager.addJob( |
| | | ElectronicEffectJob.class, |
| | | (ElectronicEffectJob.effect + xpElectronicFence.getId()).toUpperCase(), |
| | | TimeJobType.ELECTRONIC_EFFECT, |
| | | DateUtils.localDateTimeToDate(dto.getEffectTime()), |
| | | maps |
| | | ); |
| | | xpElectronicFence.setStatus(1); |
| | | } |
| | | } |
| | | if (dto.getFailureType() != null && dto.getFailureType() == 2) { |
| | | |
| | | if (dto.getFailureTime().isBefore(LocalDateTime.now())) { |
| | | // 设置待启用 |
| | | xpElectronicFence.setStatus(1); |
| | | } else { |
| | | // 添加定时任务job type=1电子围栏生效 type=2电子围栏失效 |
| | | Map<String, ? extends Object> maps = |
| | | new ImmutableMap.Builder<String, Long>(). |
| | | put("type", 2L) |
| | | .put("id", xpElectronicFence.getId()) |
| | | .build(); |
| | | QuartzManager.addJob( |
| | | ElectronicEffectJob.class, |
| | | (ElectronicEffectJob.failure + xpElectronicFence.getId()).toUpperCase(), |
| | | TimeJobType.ELECTRONIC_FAILURE, |
| | | DateUtils.localDateTimeToDate(dto.getEffectTime()), |
| | | maps |
| | | ); |
| | | xpElectronicFence.setStatus(1); |
| | | } |
| | | } |
| | | } |
| | | this.updateById(xpElectronicFence); |
| | | if (xpElectronicFence.getStatus() == 2) { |
| | | // 设置电子围栏ID并保存区域数据 |
| | | for (XpElectronicFenceArea area : newElectronicFenceAreas) { |
| | | area.setElectronicId(xpElectronicFence.getId()); |
| | | area.setAreaStatus(1); |
| | | area.setArea(area.getArea()); |
| | | area.setCityCode(dto.getCityCode()); |
| | | xpElectronicFenceAreaMapper.insert(area); |
| | | } |
| | | |
| | | }else{ |
| | | // 设置电子围栏ID并保存区域数据 |
| | | for (XpElectronicFenceArea area : newElectronicFenceAreas) { |
| | | area.setElectronicId(xpElectronicFence.getId()); |
| | | area.setAreaStatus(2); |
| | | area.setArea(area.getArea()); |
| | | area.setCityCode(dto.getCityCode()); |
| | | xpElectronicFenceAreaMapper.insert(area); |
| | | |
| | | } |
| | | } |
| | | |
| | | if (xpElectronicFence.getStatus() == 2) { |
| | | for (XpElectronicFenceArea newElectronicFenceArea : newElectronicFenceAreas) { |
| | | if (newElectronicFenceArea.getAreaType() == 1 |
| | | && newElectronicFenceArea.getAreaStatus() == 1) { |
| | | XpGeoFencingPolygon xpGeoFencingPolygon = new XpGeoFencingPolygon(); |
| | | xpGeoFencingPolygon.setFenceId(String.valueOf(xpElectronicFence.getId())); |
| | | xpGeoFencingPolygon.setName(xpElectronicFence.getElectronicName()); |
| | | xpGeoFencingPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId())); |
| | | xpGeoFencingPolygon.setCityCode(xpElectronicFence.getCityCode()); |
| | | xpGeoFencingPolygon.setOperationStrategy(xpElectronicFence.getOperationStrategy()); |
| | | // 将围栏点转换为GeoJSON多边形 |
| | | GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints()); |
| | | xpGeoFencingPolygon.setPoints(geoJsonPolygon); |
| | | mongoTemplate.insert(xpGeoFencingPolygon); |
| | | } |
| | | if (newElectronicFenceArea.getAreaType() == 2 |
| | | && newElectronicFenceArea.getAreaStatus() == 1) { |
| | | XpGeoFencingProhibitedPolygon xpGeoFencingProhibitedPolygon = new XpGeoFencingProhibitedPolygon(); |
| | | xpGeoFencingProhibitedPolygon.setFenceId(String.valueOf(xpElectronicFence.getId())); |
| | | xpGeoFencingProhibitedPolygon.setName(xpElectronicFence.getElectronicName()); |
| | | xpGeoFencingProhibitedPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId())); |
| | | // 将围栏点转换为GeoJSON多边形 |
| | | GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints()); |
| | | xpGeoFencingProhibitedPolygon.setPoints(geoJsonPolygon); |
| | | mongoTemplate.save(xpGeoFencingProhibitedPolygon); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return R.ok(); |
| | | } catch (Exception e) { |
| | | log.error("新增电子围栏失败", e); |
| | | throw new ServiceException("新增电子围栏失败", I18nKeyConstant.ADD_ELECTRONIC_ERROR); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public R editElectronic(ElectronicFenceUpdateDto dto) { |
| | | try { |
| | | // 获取现有的运营区域(areaType=1),排除当前正在编辑的电子围栏 |
| | | LambdaQueryWrapper<XpElectronicFenceArea> xpElectronicFenceAreaLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
| | | xpElectronicFenceAreaLambdaQueryWrapper.eq(XpElectronicFenceArea::getAreaType, 1); |
| | | xpElectronicFenceAreaLambdaQueryWrapper.ne(XpElectronicFenceArea::getElectronicId, dto.getId()); |
| | | List<XpElectronicFenceArea> existingOperationAreas = xpElectronicFenceAreaMapper.selectList(xpElectronicFenceAreaLambdaQueryWrapper); |
| | | |
| | | // 获取待更新的区域 |
| | | List<XpElectronicFenceArea> updatedElectronicFenceAreas = dto.getElectronicFenceAreas(); |
| | | |
| | | // 检查更新后的区域与现有运营区域是否有重叠 |
| | | for (XpElectronicFenceArea updatedArea : updatedElectronicFenceAreas) { |
| | | if (updatedArea.getAreaType() == 1) { // 只检查运营区域 |
| | | for (XpElectronicFenceArea existingArea : existingOperationAreas) { |
| | | if (polygonOverlapService.checkPolygonOverlap( |
| | | updatedArea.getFencePoints(), |
| | | existingArea.getFencePoints())) { |
| | | throw new ServiceException( |
| | | "运营区域{}与现有运营区域{}发生重叠", |
| | | I18nKeyConstant.AREA_OVERLAP, |
| | | new Object[]{existingArea.getAreaName(), existingArea.getAreaName()} |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | // 更新主表数据 |
| | | XpElectronicFence xpElectronicFence = new XpElectronicFence(); |
| | | BeanUtils.copyProperties(dto, xpElectronicFence); |
| | | if (dto.getStatus() == 0) { |
| | | xpElectronicFence.setStatus(0); |
| | | } else if (dto.getStatus() == 1) { |
| | | if (dto.getEffectType() == 1) { |
| | | // 设置为启用中 |
| | | xpElectronicFence.setStatus(2); |
| | | xpElectronicFence.setEffectTime(LocalDateTime.now()); |
| | | } else { |
| | | if (dto.getEffectTime().isBefore(LocalDateTime.now())) { |
| | | // 设置为启用中 |
| | | xpElectronicFence.setStatus(2); |
| | | } else { |
| | | // 先删除以前的定时任务 |
| | | QuartzManager.removeJob((ElectronicEffectJob.effect + xpElectronicFence.getId()).toUpperCase(), TimeJobType.ELECTRONIC_EFFECT); |
| | | |
| | | // 添加定时任务job type=1电子围栏生效 type=2电子围栏失效 |
| | | Map<String, ? extends Object> maps = |
| | | new ImmutableMap.Builder<String, Long>(). |
| | | put("type", 1L) |
| | | .put("id", xpElectronicFence.getId()) |
| | | .build(); |
| | | QuartzManager.addJob( |
| | | ElectronicEffectJob.class, |
| | | (ElectronicEffectJob.effect + xpElectronicFence.getId()).toUpperCase(), |
| | | TimeJobType.ELECTRONIC_EFFECT, |
| | | DateUtils.localDateTimeToDate(dto.getEffectTime()), |
| | | maps |
| | | ); |
| | | xpElectronicFence.setStatus(1); |
| | | } |
| | | } |
| | | if (dto.getFailureType() != null && dto.getFailureType() == 2) { |
| | | |
| | | if (dto.getFailureTime().isBefore(LocalDateTime.now())) { |
| | | // 设置待启用 |
| | | xpElectronicFence.setStatus(1); |
| | | } else { |
| | | // 先删除以前的定时任务 |
| | | QuartzManager.removeJob((ElectronicEffectJob.failure + xpElectronicFence.getId()).toUpperCase(), TimeJobType.ELECTRONIC_FAILURE); |
| | | // 添加定时任务job type=1电子围栏生效 type=2电子围栏失效 |
| | | Map<String, ? extends Object> maps = |
| | | new ImmutableMap.Builder<String, Long>(). |
| | | put("type", 2L) |
| | | .put("id", xpElectronicFence.getId()) |
| | | .build(); |
| | | QuartzManager.addJob( |
| | | ElectronicEffectJob.class, |
| | | (ElectronicEffectJob.failure + xpElectronicFence.getId()).toUpperCase(), |
| | | TimeJobType.ELECTRONIC_FAILURE, |
| | | DateUtils.localDateTimeToDate(dto.getEffectTime()), |
| | | maps |
| | | ); |
| | | xpElectronicFence.setStatus(1); |
| | | } |
| | | } |
| | | } |
| | | xpElectronicFence.setCityCode(dto.getCityCode()); |
| | | |
| | | this.updateById(xpElectronicFence); |
| | | // 删除旧的区域数据,然后保存新的区域数据 |
| | | xpElectronicFenceAreaMapper.delete(new LambdaQueryWrapper<XpElectronicFenceArea>() |
| | | .eq(XpElectronicFenceArea::getElectronicId, dto.getId())); |
| | | if (xpElectronicFence.getStatus() == 2) { |
| | | // 设置电子围栏ID并保存区域数据 |
| | | for (XpElectronicFenceArea area : updatedElectronicFenceAreas) { |
| | | area.setElectronicId(xpElectronicFence.getId()); |
| | | area.setAreaStatus(1); |
| | | area.setArea(area.getArea()); |
| | | area.setCityCode(dto.getCityCode()); |
| | | xpElectronicFenceAreaMapper.insert(area); |
| | | } |
| | | }else{ |
| | | // 设置电子围栏ID并保存区域数据 |
| | | for (XpElectronicFenceArea area : updatedElectronicFenceAreas) { |
| | | area.setElectronicId(xpElectronicFence.getId()); |
| | | area.setAreaStatus(2); |
| | | area.setArea(area.getArea()); |
| | | area.setCityCode(dto.getCityCode()); |
| | | xpElectronicFenceAreaMapper.insert(area); |
| | | } |
| | | } |
| | | if (dto.getStatus() == 2) { |
| | | for (XpElectronicFenceArea newElectronicFenceArea : updatedElectronicFenceAreas) { |
| | | if (newElectronicFenceArea.getAreaType() == 1 |
| | | && newElectronicFenceArea.getAreaStatus() == 1) { |
| | | // 先查询出mongo里围栏相同的数据 如果有 那么更新数据 |
| | | Query query = new Query(); |
| | | query.addCriteria(Criteria.where("fenceId").is(String.valueOf(xpElectronicFence.getId()))); |
| | | query.addCriteria(Criteria.where("areaId").is(String.valueOf(newElectronicFenceArea.getId()))); |
| | | XpGeoFencingPolygon old = mongoTemplate.findOne(query, XpGeoFencingPolygon.class); |
| | | if (old == null) { |
| | | XpGeoFencingPolygon xpGeoFencingPolygon = new XpGeoFencingPolygon(); |
| | | xpGeoFencingPolygon.setFenceId(String.valueOf(xpElectronicFence.getId())); |
| | | xpGeoFencingPolygon.setName(xpElectronicFence.getElectronicName()); |
| | | xpGeoFencingPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId())); |
| | | xpGeoFencingPolygon.setOperationStrategy(xpElectronicFence.getOperationStrategy()); |
| | | // 将围栏点转换为GeoJSON多边形 |
| | | GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints()); |
| | | xpGeoFencingPolygon.setPoints(geoJsonPolygon); |
| | | mongoTemplate.insert(xpGeoFencingPolygon); |
| | | } else { |
| | | // 如果存在,则更新已有文档 |
| | | old.setFenceId(String.valueOf(xpElectronicFence.getId())); |
| | | old.setName(xpElectronicFence.getElectronicName()); |
| | | old.setAreaId(String.valueOf(newElectronicFenceArea.getId())); |
| | | old.setOperationStrategy(xpElectronicFence.getOperationStrategy()); |
| | | // 将围栏点转换为GeoJSON多边形 |
| | | GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints()); |
| | | old.setPoints(geoJsonPolygon); |
| | | // 执行更新操作 |
| | | mongoTemplate.save(old); // save() 会根据 _id 自动判断是插入还是更新 |
| | | } |
| | | |
| | | } |
| | | if (newElectronicFenceArea.getAreaType() == 2 |
| | | && newElectronicFenceArea.getAreaStatus() == 1) { |
| | | // 先查询出mongo里围栏相同的数据 如果有 那么更新数据 |
| | | Query query = new Query(); |
| | | query.addCriteria(Criteria.where("fenceId").is(String.valueOf(xpElectronicFence.getId()))); |
| | | query.addCriteria(Criteria.where("areaId").is(String.valueOf(newElectronicFenceArea.getId()))); |
| | | XpGeoFencingProhibitedPolygon old = mongoTemplate.findOne(query, XpGeoFencingProhibitedPolygon.class); |
| | | if (old == null) { |
| | | XpGeoFencingProhibitedPolygon xpGeoFencingProhibitedPolygon = new XpGeoFencingProhibitedPolygon(); |
| | | xpGeoFencingProhibitedPolygon.setFenceId(String.valueOf(xpElectronicFence.getId())); |
| | | xpGeoFencingProhibitedPolygon.setName(xpElectronicFence.getElectronicName()); |
| | | xpGeoFencingProhibitedPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId())); |
| | | // 将围栏点转换为GeoJSON多边形 |
| | | GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints()); |
| | | xpGeoFencingProhibitedPolygon.setPoints(geoJsonPolygon); |
| | | mongoTemplate.save(xpGeoFencingProhibitedPolygon); |
| | | } else { |
| | | old.setFenceId(String.valueOf(xpElectronicFence.getId())); |
| | | old.setName(xpElectronicFence.getElectronicName()); |
| | | old.setAreaId(String.valueOf(newElectronicFenceArea.getId())); |
| | | // 将围栏点转换为GeoJSON多边形 |
| | | GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints()); |
| | | old.setPoints(geoJsonPolygon); |
| | | mongoTemplate.save(old); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | // 判断区域下的所有运营点位是否处于运营区域内 如果不是则更新为测试点位 |
| | | List<Long> areaIds = updatedElectronicFenceAreas.stream().map(XpElectronicFenceArea::getId).collect(Collectors.toList()); |
| | | LambdaQueryWrapper<XpPoint> pointLambdaQueryWrapper = new LambdaQueryWrapper<>(); |
| | | pointLambdaQueryWrapper.in(XpPoint::getElectronicAreaId, areaIds).eq(XpPoint::getPointType, 1); |
| | | List<XpPoint> xpPoints = xpPointMapper.selectList(pointLambdaQueryWrapper); |
| | | List<XpPoint> updatePoints = new ArrayList<>(); |
| | | if (!xpPoints.isEmpty()) { |
| | | List<Long> electronicAreaId = xpPoints.stream().map(XpPoint::getElectronicAreaId).distinct().collect(Collectors.toList()); |
| | | if (!electronicAreaId.isEmpty()) { |
| | | Map<Long, XpElectronicFenceArea> longXpElectronicFenceAreaMap = updatedElectronicFenceAreas |
| | | .stream().collect(Collectors.toMap(XpElectronicFenceArea::getId, e -> e)); |
| | | for (XpPoint xpPoint : xpPoints) { |
| | | XpElectronicFenceArea xpElectronicFenceArea = longXpElectronicFenceAreaMap.get(xpPoint.getElectronicAreaId()); |
| | | |
| | | if (xpElectronicFenceArea != null) { |
| | | String fencePoints = xpElectronicFenceArea.getFencePoints(); |
| | | Double pointLat = xpPoint.getPointLat(); |
| | | Double pointLon = xpPoint.getPointLon(); |
| | | // 判断这个点位有没有处于当前区域 由多个经纬度点构成的面里 |
| | | if (fencePoints != null && !fencePoints.trim().isEmpty() && |
| | | pointLat != null && pointLon != null) { |
| | | boolean isInside = isPointInPolygon(fencePoints, pointLon, pointLat); |
| | | // 如果点不在区域内,更新为测试点位 |
| | | if (!isInside) { |
| | | xpPoint.setPointType(2); // |
| | | log.info("点位ID: {} 经纬度: ({}, {}) 不在电子围栏区域内,已更新为测试点位", |
| | | xpPoint.getId(), pointLon, pointLat); |
| | | UpdateWrapper<XpPoint> xpPointUpdateWrapper = new UpdateWrapper<>(); |
| | | xpPointUpdateWrapper.eq("id", xpPoint.getId()); |
| | | xpPointUpdateWrapper.set("point_type", 2); |
| | | xpPointUpdateWrapper.set("electronic_areaId", null); |
| | | xpPointMapper.update(xpPoint, xpPointUpdateWrapper); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return R.ok(); |
| | | } catch (Exception e) { |
| | | log.error("编辑电子围栏失败", e); |
| | | throw new ServiceException("编辑电子围栏失败", I18nKeyConstant.EDIT_ELECTRONIC_ERROR); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 判断点是否在多边形内 |
| | | * |
| | | * @param fencePointsStr 多边形顶点字符串,格式:经度_维度,经度_维度 |
| | | * @param pointLon 待检测点的经度 |
| | | * @param pointLat 待检测点的纬度 |
| | | * @return true表示在多边形内,false表示在多边形外 |
| | | */ |
| | | private boolean isPointInPolygon(String fencePointsStr, double pointLon, double pointLat) { |
| | | if (fencePointsStr == null || fencePointsStr.trim().isEmpty()) { |
| | | return false; |
| | | } |
| | | |
| | | // 解析多边形顶点 |
| | | String[] pointPairs = fencePointsStr.split(","); |
| | | List<double[]> polygonVertices = new ArrayList<>(); |
| | | |
| | | for (String pointPair : pointPairs) { |
| | | String[] lngLat = pointPair.trim().split("_"); |
| | | if (lngLat.length >= 2) { |
| | | try { |
| | | double longitude = Double.parseDouble(lngLat[0]); |
| | | double latitude = Double.parseDouble(lngLat[1]); |
| | | polygonVertices.add(new double[]{longitude, latitude}); |
| | | } catch (NumberFormatException e) { |
| | | log.error("解析多边形顶点坐标失败: {}", pointPair, e); |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (polygonVertices.size() < 3) { |
| | | return false; // 至少需要3个点才能构成多边形 |
| | | } |
| | | |
| | | return isPointInPolygon(polygonVertices, pointLon, pointLat); |
| | | } |
| | | |
| | | /** |
| | | * 使用射线法判断点是否在多边形内 |
| | | * |
| | | * @param polygonVertices 多边形顶点列表 |
| | | * @param pointLon 待检测点的经度 |
| | | * @param pointLat 待检测点的纬度 |
| | | * @return true表示在多边形内,false表示在多边形外 |
| | | */ |
| | | private boolean isPointInPolygon(List<double[]> polygonVertices, double pointLon, double pointLat) { |
| | | int n = polygonVertices.size(); |
| | | boolean inside = false; |
| | | |
| | | // 射线法判断点是否在多边形内 |
| | | double x = pointLon, y = pointLat; |
| | | |
| | | for (int i = 0, j = n - 1; i < n; j = i++) { |
| | | double xi = polygonVertices.get(i)[0], yi = polygonVertices.get(i)[1]; |
| | | double xj = polygonVertices.get(j)[0], yj = polygonVertices.get(j)[1]; |
| | | |
| | | // 判断当前边是否与水平射线相交 |
| | | boolean intersect = ((yi > y) != (yj > y)) && |
| | | (x < (xj - xi) * (y - yi) / (yj - yi) + xi); |
| | | |
| | | if (intersect) { |
| | | inside = !inside; |
| | | } |
| | | } |
| | | |
| | | return inside; |
| | | } |
| | | |
| | | /** |
| | | * 将围栏点字符串转换为GeoJsonPolygon |
| | | * 格式:经度_维度,经度_维度 |
| | | */ |
| | | private GeoJsonPolygon convertToGeoJsonPolygon(String fencePointsStr) { |
| | | if (fencePointsStr == null || fencePointsStr.trim().isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | // 分割多个点对 |
| | | String[] pointPairs = fencePointsStr.split(","); |
| | | List<Point> points = new ArrayList<>(); |
| | | |
| | | for (String pointPair : pointPairs) { |
| | | String[] lngLat = pointPair.trim().split("_"); |
| | | if (lngLat.length >= 2) { |
| | | try { |
| | | double longitude = Double.parseDouble(lngLat[0]); |
| | | double latitude = Double.parseDouble(lngLat[1]); |
| | | points.add(new Point(longitude, latitude)); // Point 构造函数是 (x, y) -> (longitude, latitude) |
| | | } catch (NumberFormatException e) { |
| | | log.error("解析经纬度坐标失败: {}", pointPair, e); |
| | | throw new IllegalArgumentException("经纬度格式错误: " + pointPair); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 如果没有足够的点构成多边形,至少需要3个点 |
| | | if (points.size() < 3) { |
| | | throw new IllegalArgumentException("围栏点数量不足,至少需要3个点构成多边形"); |
| | | } |
| | | |
| | | // 确保多边形是闭合的(如果最后一个点不等于第一个点,则添加第一个点) |
| | | if (!points.get(0).equals(points.get(points.size() - 1))) { |
| | | points.add(points.get(0)); // 添加第一个点作为最后一个点,闭合多边形 |
| | | } |
| | | |
| | | return new GeoJsonPolygon(points); |
| | | } |
| | | |
| | | /** |
| | | * 检查新添加的区域之间是否有重叠 |
| | | */ |
| | | private boolean hasOverlapWithinNewAreas(List<XpElectronicFenceArea> newAreas) { |
| | | for (int i = 0; i < newAreas.size(); i++) { |
| | | XpElectronicFenceArea area1 = newAreas.get(i); |
| | | if (area1.getAreaType() != 1) continue; // 只检查运营区域 |
| | | |
| | | for (int j = i + 1; j < newAreas.size(); j++) { |
| | | XpElectronicFenceArea area2 = newAreas.get(j); |
| | | if (area2.getAreaType() == 1) { |
| | | if (polygonOverlapService.checkPolygonOverlap( |
| | | area1.getFencePoints(), |
| | | area2.getFencePoints())) { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * @return |
| | | */ |
| | | @Override |
| | | public List<XpElectronicFence> getAvailableList() { |
| | | return lambdaQuery().eq(XpElectronicFence::getStatus, 2).list(); //查询已启用的围栏 |
| | | } |
| | | |
| | | @Override |
| | | public List<XpElectronicFence> getList() { |
| | | return list(); |
| | | } |
| | | |
| | | |
| | | } |