From 246c72b8e1cba97de66efd8112ed166210ebc686 Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期五, 17 四月 2026 12:02:38 +0800
Subject: [PATCH] fix:围栏点位重名校验、围栏禁行区禁用

---
 xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/constant/I18nKeyConstant.java                         |    4 ++
 xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java           |    1 
 xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java           |   15 +++++++
 xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java |   31 ++++++++++++++-
 xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpElectronicFenceService.java                 |    5 ++
 xp-robotaxi-map-boot/src/main/resources/i18n/messages.properties                                          |    2 +
 xp-robotaxi-map-boot/src/main/resources/i18n/messages_zh_CN.properties                                    |    2 +
 xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpPointService.java                           |    5 ++
 xp-robotaxi-map-boot/src/main/resources/i18n/messages_en_US.properties                                    |    2 +
 9 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/constant/I18nKeyConstant.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/constant/I18nKeyConstant.java
index 4c98db4..5927541 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/constant/I18nKeyConstant.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/constant/I18nKeyConstant.java
@@ -31,6 +31,8 @@
     String FENCE_STRUCTURE_NOT_EXIST = "fence.structure.not.exist";
     // 围栏不存在
     String FENCE_NOT_EXIST = "fence.not.exist";
+    // 围栏名称已存在
+    String FENCE_NAME_EXISTS = "fence.name.exists";
     // 围栏启用状态不可复制
     String FENCE_STATUS_CANNOT_COPIED = "fence.status.cannot.copied";
 
@@ -97,6 +99,8 @@
     String POINT_NUMBER_LOCK_FAIL = "point.number.lock.fail";
     // 点位-编号解析失败
     String POINT_NUMBER_PARSE_FAIL = "point.number.parse.fail";
+    // 点位-名称已存在
+    String POINT_NAME_EXISTS = "point.name.exists";
 
     // 点位删除-存在关联巡游点位
     String POINT_DELETE_HAS_CRUISE_PLAN_POINT = "point.delete.has.cruise.plan.point";
diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java
index 33bf88f..4ae9619 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpPointManagementController.java
@@ -204,6 +204,7 @@
         if (xpPoint == null) {
             throw new ServiceException("系统异常", I18nKeyConstant.SYSTEM_EXCEPTION);
         }
+        xpPointService.validatePointNameUnique(pointAddDto.getPointName(), pointAddDto.getId());
         BeanUtils.copyProperties(pointAddDto, xpPoint);
         if (Integer.valueOf(2).equals(pointAddDto.getOperationStatus()) && Integer.valueOf(5).equals(pointAddDto.getVerificationStatus())) {
             Long electronicAreaId = xpPoint.getElectronicAreaId();
diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpElectronicFenceService.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpElectronicFenceService.java
index b58fc71..c7ceb23 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpElectronicFenceService.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpElectronicFenceService.java
@@ -27,6 +27,11 @@
     R editElectronic(ElectronicFenceUpdateDto dto);
 
     /**
+     * 校验围栏名称唯一
+     */
+    void validateElectronicNameUnique(String electronicName, Long excludeId);
+
+    /**
      * 查询启用的电子围栏列表
      * @return
      */
diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpPointService.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpPointService.java
index dbc3b4a..8dbdd7d 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpPointService.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/XpPointService.java
@@ -39,6 +39,11 @@
     XpPoint addPointWithGeneratedNumber(@Valid PointAddDto pointAddDto);
 
     /**
+     * 校验点位名称唯一
+     */
+    void validatePointNameUnique(String pointName, Long excludeId);
+
+    /**
      * 获取附近上车站点
      */
     NearestVo nearest(@Valid NearestDto dto);
diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
index a54e6b9..dcf87ac 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpElectronicFenceServiceImpl.java
@@ -211,6 +211,7 @@
     @Transactional
     @ShardingSphereTransactionType(TransactionType.BASE)
     public R addElectronic(ElectronicFenceAddDto dto) {
+        validateElectronicNameUnique(dto.getElectronicName(), null);
         // 获取现有的运营区域(areaType=1)
         LambdaQueryWrapper<XpElectronicFenceArea> xpElectronicFenceAreaLambdaQueryWrapper = new LambdaQueryWrapper<>();
         xpElectronicFenceAreaLambdaQueryWrapper.eq(XpElectronicFenceArea::getAreaType, 1);
@@ -343,6 +344,7 @@
     @Transactional
     @ShardingSphereTransactionType(TransactionType.BASE)
     public R editElectronic(ElectronicFenceUpdateDto dto) {
+        validateElectronicNameUnique(dto.getElectronicName(), dto.getId());
         // 获取现有的运营区域(areaType=1),排除当前正在编辑的电子围栏
         LambdaQueryWrapper<XpElectronicFenceArea> xpElectronicFenceAreaLambdaQueryWrapper = new LambdaQueryWrapper<>();
         xpElectronicFenceAreaLambdaQueryWrapper.eq(XpElectronicFenceArea::getAreaType, 1);
@@ -448,7 +450,9 @@
             // 设置电子围栏ID并保存区域数据
             for (XpElectronicFenceArea area : updatedElectronicFenceAreas) {
                 area.setElectronicId(xpElectronicFence.getId());
-                area.setAreaStatus(1);
+                if (area.getAreaType() == null || area.getAreaType() == 1) {
+                    area.setAreaStatus(1);
+                }
                 area.setArea(area.getArea());
                 area.setCityCode(dto.getCityCode());
                 if (null != area.getId()) {
@@ -461,7 +465,9 @@
             // 设置电子围栏ID并保存区域数据
             for (XpElectronicFenceArea area : updatedElectronicFenceAreas) {
                 area.setElectronicId(xpElectronicFence.getId());
-                area.setAreaStatus(2);
+                if (area.getAreaType() == null || area.getAreaType() == 1) {
+                    area.setAreaStatus(2);
+                }
                 area.setArea(area.getArea());
                 area.setCityCode(dto.getCityCode());
                 if (null != area.getId()) {
@@ -531,6 +537,12 @@
                         old.setPoints(geoJsonPolygon);
                         mongoTemplate.save(old);
                     }
+                } else if (newElectronicFenceArea.getAreaType() == 2
+                        && newElectronicFenceArea.getId() != null) {
+                    Query query = new Query();
+                    query.addCriteria(Criteria.where("fenceId").is(String.valueOf(xpElectronicFence.getId())));
+                    query.addCriteria(Criteria.where("areaId").is(String.valueOf(newElectronicFenceArea.getId())));
+                    mongoTemplate.remove(query, XpGeoFencingProhibitedPolygon.class);
                 }
             }
         }
@@ -749,6 +761,21 @@
         return R.ok();
 
     }
+
+    @Override
+    public void validateElectronicNameUnique(String electronicName, Long excludeId) {
+        if (!StringUtils.hasText(electronicName)) {
+            return;
+        }
+        long count = this.lambdaQuery()
+                .eq(XpElectronicFence::getElectronicName, electronicName)
+                .ne(excludeId != null, XpElectronicFence::getId, excludeId)
+                .count();
+        if (count > 0) {
+            throw new ServiceException("围栏名称已存在", I18nKeyConstant.FENCE_NAME_EXISTS);
+        }
+    }
+
     private static List<Long> normalizePointIds(List<Long> ids) {
         if (CollUtil.isEmpty(ids)) {
             return Collections.emptyList();
diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java
index 0cffd2f..e013adf 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java
@@ -107,6 +107,7 @@
         if (pointAddDto.getElectronicAreaId() == null) {
             throw new ServiceException("运营区域ID不能为空", I18nKeyConstant.POINT_ELECTRONIC_AREA_ID_EMPTY);
         }
+        validatePointNameUnique(pointAddDto.getPointName(), null);
 
         String cityPrefix = buildCityPrefix(pointAddDto.getElectronicAreaId());
         String lockKey = buildPointNumberLockKey(cityPrefix, POINT_BIZ_TYPE);
@@ -147,6 +148,20 @@
     }
 
     @Override
+    public void validatePointNameUnique(String pointName, Long excludeId) {
+        if (!StringUtils.hasText(pointName)) {
+            return;
+        }
+        long count = this.lambdaQuery()
+                .eq(XpPoint::getPointName, pointName)
+                .ne(excludeId != null, XpPoint::getId, excludeId)
+                .count();
+        if (count > 0) {
+            throw new ServiceException("点位名称已存在", I18nKeyConstant.POINT_NAME_EXISTS);
+        }
+    }
+
+    @Override
     public NearestVo nearest(NearestDto dto) {
         // 走缓存
         Object orderDistanceObj = redisService.getCacheObject(CacheConstants.NEAR_POINT_DISTANCE);
diff --git a/xp-robotaxi-map-boot/src/main/resources/i18n/messages.properties b/xp-robotaxi-map-boot/src/main/resources/i18n/messages.properties
index 1138d8c..51c612e 100644
--- a/xp-robotaxi-map-boot/src/main/resources/i18n/messages.properties
+++ b/xp-robotaxi-map-boot/src/main/resources/i18n/messages.properties
@@ -10,6 +10,7 @@
 area.overlap.error=运营区域{0}与现有运营区域{1}发生重叠,请编辑已有的运营区域;
 fence.structure.not.exist=围栏构成不存在
 fence.not.exist=围栏不存在
+fence.name.exists=围栏名称已存在
 fence.status.cannot.copied=围栏启用状态不可复制;
 order.create.fail=订单创建失败
 open.city.exists=该城市已存在
@@ -39,6 +40,7 @@
 point.city.not.exist=城市信息不存在
 point.number.lock.fail=点位编号生成繁忙,请稍后重试
 point.number.parse.fail=点位编号生成失败(历史编号格式异常)
+point.name.exists=点位名称已存在
 point.delete.has.cruise.plan.point=操作失败,该点位有关联的巡游计划点位
 point.enable.not.in.operating.area=当前点位不在围栏运营区域内,不能启用。
 point.enable.in.prohibited.area=当前点位在禁行区内,不能启用。
diff --git a/xp-robotaxi-map-boot/src/main/resources/i18n/messages_en_US.properties b/xp-robotaxi-map-boot/src/main/resources/i18n/messages_en_US.properties
index 74c82cc..c288015 100644
--- a/xp-robotaxi-map-boot/src/main/resources/i18n/messages_en_US.properties
+++ b/xp-robotaxi-map-boot/src/main/resources/i18n/messages_en_US.properties
@@ -11,6 +11,7 @@
 fence.structure.not.exist=The fence composition does not exist\
 fence.status.cannot.copied=The enabled state of the fence cannot be copied;
 fence.not.exist=The fence does not exist.
+fence.name.exists=Fence name already exists
 order.create.fail=Order creation failed
 open.city.exists=This city already exists
 pricing.exists=Pricing for this vehicle model/channel/city already exists
@@ -32,6 +33,7 @@
 point.city.not.exist=City information does not exist
 point.number.lock.fail=Point number generation is busy, please try again later
 point.number.parse.fail=Point number generation failed (invalid historical number format)
+point.name.exists=Point name already exists
 point.delete.has.cruise.plan.point=Operation failed. This point is associated with cruise plan points.
 point.enable.not.in.operating.area=This point is not within the fence operating area and cannot be enabled.
 point.enable.in.prohibited.area=This point is in a prohibited area and cannot be enabled.
diff --git a/xp-robotaxi-map-boot/src/main/resources/i18n/messages_zh_CN.properties b/xp-robotaxi-map-boot/src/main/resources/i18n/messages_zh_CN.properties
index 2a0d82f..724440c 100644
--- a/xp-robotaxi-map-boot/src/main/resources/i18n/messages_zh_CN.properties
+++ b/xp-robotaxi-map-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -10,6 +10,7 @@
 area.overlap.error=运营区域{0}与现有运营区域{1}发生重叠,请编辑已有的运营区域;
 fence.structure.not.exist=围栏构成不存在
 fence.not.exist=围栏不存在
+fence.name.exists=围栏名称已存在
 fence.status.cannot.copied=围栏启用状态不可复制
 order.create.fail=订单创建失败
 open.city.exists=该城市已存在
@@ -32,6 +33,7 @@
 point.city.not.exist=城市信息不存在
 point.number.lock.fail=点位编号生成繁忙,请稍后重试
 point.number.parse.fail=点位编号生成失败(历史编号格式异常)
+point.name.exists=点位名称已存在
 point.delete.has.cruise.plan.point=操作失败,该点位有关联的巡游计划点位
 point.enable.not.in.operating.area=当前点位不在围栏运营区域内,不能启用。
 point.enable.in.prohibited.area=当前点位在禁行区内,不能启用。

--
Gitblit v1.7.1