From c96fbe6b0aeb413a17c429133ee6d81d138144b9 Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期二, 09 六月 2026 10:58:52 +0800
Subject: [PATCH] Merge branch '1.0' of http://120.76.84.145:10101/gitblit/r/java/Robotaxi into 1.0
---
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 275 insertions(+), 20 deletions(-)
diff --git a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java
index 208c58c..62cd452 100644
--- a/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java
+++ b/xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java
@@ -2,44 +2,61 @@
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xiaopeng.api.dto.operation.ElectronicFenceAddDto;
import com.xiaopeng.api.dto.operation.ElectronicFenceAreaUpdateStatusDto;
+import com.xiaopeng.api.dto.operation.ElectronicFenceOperationStrategyValidateDto;
import com.xiaopeng.api.dto.operation.ElectronicFenceUpdateDto;
-import com.xiaopeng.api.feignClient.operation.XpElectronicClient;
-import com.xiaopeng.api.model.operation.XpElectronicFence;
-import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
-import com.xiaopeng.api.model.operation.XpPoint;
-import com.xiaopeng.api.model.operation.XpRegion;
+import com.xiaopeng.api.model.operation.*;
import com.xiaopeng.api.query.operation.ElectronicPageListQuery;
+import com.xiaopeng.api.query.operation.PointListQuery;
+import com.xiaopeng.api.query.operation.XpStationQuery;
import com.xiaopeng.api.vo.operation.ElectronicFenceDetailVo;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.bean.BeanUtils;
import com.xiaopeng.common.core.web.page.PageInfo;
-import com.xiaopeng.common.security.service.TokenService;
+import com.xiaopeng.common.log.annotation.AdminOperationLog;
+import com.xiaopeng.common.log.enums.AdminBusinessType;
+import com.xiaopeng.common.log.enums.AdminOperationType;
import com.xiaopeng.map.constant.I18nKeyConstant;
-import com.xiaopeng.map.service.XpElectronicFenceAreaService;
-import com.xiaopeng.map.service.XpElectronicFenceService;
-import com.xiaopeng.map.service.XpRegionService;
+import com.xiaopeng.map.service.*;
+import com.xiaopeng.map.service.serviceimpl.BootCommonService;
+import com.xiaopeng.map.utils.OperationStrategyIndex;
+import com.xiaopeng.api.query.operation.FenceAndStationVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
-import org.locationtech.jts.geom.Coordinate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import static com.xiaopeng.map.service.serviceimpl.XpElectronicFenceServiceImpl.getPinyinInitials;
+import com.xiaopeng.common.log.annotation.AdminOperationLog;
+import com.xiaopeng.common.log.enums.AdminBusinessType;
+import com.xiaopeng.common.log.enums.AdminOperationType;
/**
* <p>
@@ -66,6 +83,14 @@
private XpRegionService regionService;
@Autowired
private XpRegionService xpRegionService;
+
+ @Autowired
+ private XpPointService xpPointService;
+ @Resource
+ private XpStationService xpStationService;
+ @Resource
+ private BootCommonService bootCommonService;
+
/**
* 查询省市管理
*/
@@ -74,6 +99,20 @@
public R<List<List<XpRegion>>> geXpRegion() {
return R.ok(regionService.getXpRegion());
}
+
+ @ApiOperation(value = "获取围栏和场站数据",tags = {"管理后台-电子围栏"})
+ @GetMapping(value = "/findListFenceAndStation")
+ public R<List<FenceAndStationVo>> findListFenceAndStation(String cityCode) {
+ return R.ok(xpPointService.findListFenceAndStation(cityCode));
+ }
+
+ @ApiOperation(value = "获取不分页的电子围栏列表",tags = {"管理后台-电子围栏"})
+ @PostMapping("/list")
+ R<List<XpElectronicFence>> getList(@RequestBody ElectronicPageListQuery query){
+ return R.ok(xpElectronicFenceService.getList(query));
+ }
+
+
@ApiOperation(value = "电子围栏管理列表", response = XpElectronicFence.class)
@PostMapping("/pageList")
public R<PageInfo<XpElectronicFence>> pageList(@RequestBody ElectronicPageListQuery query) {
@@ -85,8 +124,8 @@
*
* @return 电子围栏列表
*/
- @ApiOperation(value = "电子围栏列表接口", tags = {"运维APP-车辆管理"})
- @GetMapping("/api/v1/electronicFence/list")
+ @ApiOperation(value = "电子围栏列表接口", tags = {"运维APP-电子围栏"})
+ @GetMapping("/maintenance-app/api/v1/electronicFence/list")
public R<List<XpElectronicFence>> vehicleMap() {
List<XpElectronicFence> list = xpElectronicFenceService.list();
return R.ok(list);
@@ -96,7 +135,14 @@
*/
@PostMapping("/add")
@ApiOperation(value = "新增电子围栏")
+ @AdminOperationLog(
+ businessType = AdminBusinessType.ELECTRONIC_FENCE_MANAGEMENT,
+ operationType = AdminOperationType.ADD,
+ content = "新增电子围栏",
+ appendRequestData = true
+ )
public R add(@Validated @RequestBody ElectronicFenceAddDto dto) {
+ validateFenceTime(dto.getStatus(), dto.getEffectType(), dto.getEffectTime(), dto.getFailureType(), dto.getFailureTime());
return xpElectronicFenceService.addElectronic(dto);
}
@@ -108,6 +154,12 @@
@PostMapping("/updateStatus")
@ResponseBody
@ApiOperation(value = "修改状态")
+ @AdminOperationLog(
+ businessType = AdminBusinessType.ELECTRONIC_FENCE_MANAGEMENT,
+ operationType = AdminOperationType.EDIT,
+ content = "修改电子围栏状态",
+ appendRequestData = true
+ )
public R updateStatus(@RequestBody ElectronicFenceAreaUpdateStatusDto dto) {
XpElectronicFenceArea electronicFenceArea = xpElectronicFenceAreaService.getById(dto.getId());
if (electronicFenceArea == null) {
@@ -138,6 +190,66 @@
List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpElectronicFenceArea::getElectronicId, id)
.list();
res.setElectronicFenceAreas(electronicFenceAreaList);
+
+ //点位的总数
+ if(!electronicFenceAreaList.isEmpty()){
+ PointListQuery query1 = new PointListQuery();
+ query1.setElectronicAreaIds(electronicFenceAreaList.stream().map(XpElectronicFenceArea::getId).collect(Collectors.toList()));
+ List<XpPoint> pointList = xpPointService.findList(query1);
+
+ ElectronicFenceDetailVo.Numbers pointsNumbers = new ElectronicFenceDetailVo.Numbers();
+ if(CollUtil.isNotEmpty(pointList)){
+ LocalTime localTime = LocalTime.now();
+ //总数
+ pointsNumbers.setTotalNum(pointList.size());
+ //待测试
+ long count = pointList.stream().filter(xpPoint -> xpPoint.getOperationStatus() == 1 && xpPoint.getVerificationStatus() == 1).count();
+ pointsNumbers.setToBeTested(Integer.parseInt(count+""));
+ //测试中
+ long count2 = pointList.stream().filter(xpPoint -> xpPoint.getOperationStatus() == 1 && xpPoint.getVerificationStatus() == 2).count();
+ pointsNumbers.setTesting(Integer.parseInt(count2+""));
+ //测试不通过
+ long count3 = pointList.stream().filter(xpPoint -> xpPoint.getOperationStatus() == 1 && xpPoint.getVerificationStatus() == 4).count();
+ pointsNumbers.setTestNotPass(Integer.parseInt(count3+""));
+ //待启用
+ long count4 = pointList.stream().filter(xpPoint -> xpPoint.getOperationStatus() == 1 && (xpPoint.getVerificationStatus() == 3 || xpPoint.getVerificationStatus() == 5)).count();
+ pointsNumbers.setPendingLaunch(Integer.parseInt(count4+""));
+ //启用中
+ long count5 = pointList.stream().filter(xpPoint -> xpPoint.getOperationStatus() == 2 && xpPoint.getVerificationStatus() == 5).count();
+ pointsNumbers.setStartingUp(Integer.parseInt(count5+""));
+ //已停用
+ long count6 = pointList.stream().filter(xpPoint -> xpPoint.getOperationStatus() == 3 && xpPoint.getVerificationStatus() == 5).count();
+ pointsNumbers.setDisabled(Integer.parseInt(count6+""));
+ //已失效
+ long count7 = pointList.stream().filter(xpPoint -> xpPoint.getOperationStatus() == 4 && xpPoint.getVerificationStatus() == 5).count();
+ pointsNumbers.setExpired(Integer.parseInt(count7+""));
+ }
+ res.setPointsNumbers(pointsNumbers);
+ }
+
+ //场站总数
+ XpStationQuery query2 = new XpStationQuery();
+ query2.setElectronicId(id);
+ List<XpStation> stationList = xpStationService.findList(query2);
+
+ ElectronicFenceDetailVo.Numbers stationNumbers = new ElectronicFenceDetailVo.Numbers();
+ if(CollUtil.isNotEmpty(stationList)){
+ //status 场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效
+ stationNumbers.setTotalNum(stationList.size());
+ //待生效
+ long count1 = stationList.stream().filter(xpStation -> xpStation.getStatus() == 1).count();
+ stationNumbers.setPendingLaunch(Integer.parseInt(count1+""));
+ //启用中
+ long count2 = stationList.stream().filter(xpStation -> xpStation.getStatus() == 2).count();
+ stationNumbers.setStartingUp(Integer.parseInt(count2+""));
+ //已停用
+ long count3 = stationList.stream().filter(xpStation -> xpStation.getStatus() == 3).count();
+ stationNumbers.setDisabled(Integer.parseInt(count3+""));
+ //已失效
+ long count4 = stationList.stream().filter(xpStation -> xpStation.getStatus() == 4).count();
+ stationNumbers.setExpired(Integer.parseInt(count4+""));
+ }
+ res.setStationNumbers(stationNumbers);
return R.ok(res);
}
@@ -146,8 +258,99 @@
*/
@PostMapping("/edit")
@ApiOperation(value = "编辑电子围栏")
+ @Transactional
+ @ShardingSphereTransactionType(TransactionType.BASE)
+ @AdminOperationLog(
+ businessType = AdminBusinessType.ELECTRONIC_FENCE_MANAGEMENT,
+ operationType = AdminOperationType.EDIT,
+ content = "编辑电子围栏",
+ appendRequestData = true
+ )
public R edit(@Validated @RequestBody ElectronicFenceUpdateDto dto) {
+ validateFenceTime(dto.getStatus(), dto.getEffectType(), dto.getEffectTime(), dto.getFailureType(), dto.getFailureTime());
return xpElectronicFenceService.editElectronic(dto);
+ }
+
+ /**
+ * 校验编辑围栏时运营时间段缩短影响的点位数量
+ * <p>
+ * 统计:围栏下 operation_type=2(自定义运营时间段) 的点位中,
+ * 当围栏运营时间范围发生缩短时,不在新运营时间范围(Union)内的点位数量(需要停用)。
+ * </p>
+ */
+ @PostMapping("/validateOperationStrategyShorten")
+ @ApiOperation(value = "校验围栏运营时间段缩短影响点位数量")
+ public R<Integer> validateOperationStrategyShorten(@Valid @RequestBody ElectronicFenceOperationStrategyValidateDto dto) {
+ XpElectronicFence fence = xpElectronicFenceService.getById(dto.getId());
+ if (fence == null) {
+ throw new ServiceException("围栏不存在", I18nKeyConstant.FENCE_NOT_EXIST);
+ }
+
+ OperationStrategyIndex oldIndex = OperationStrategyIndex.parse(fence.getOperationStrategy());
+ OperationStrategyIndex newIndex = OperationStrategyIndex.parse(dto.getOperationStrategy());
+ if (oldIndex.equalsNormalized(newIndex)) {
+ return R.ok(0);
+ }
+ if (!oldIndex.hasAnyShrinkComparedTo(newIndex)) {
+ return R.ok(0);
+ }
+
+ List<Long> fenceAreaIds = xpElectronicFenceAreaService.lambdaQuery()
+ .eq(XpElectronicFenceArea::getElectronicId, dto.getId())
+ .list()
+ .stream()
+ .map(XpElectronicFenceArea::getId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ if (CollUtil.isEmpty(fenceAreaIds)) {
+ return R.ok(0);
+ }
+
+ List<XpPoint> customPoints = xpPointService.lambdaQuery()
+ .in(XpPoint::getElectronicAreaId, fenceAreaIds)
+ .eq(XpPoint::getPointType, 1)
+ .eq(XpPoint::getOperationType, 2)
+ .list();
+ if (CollUtil.isEmpty(customPoints)) {
+ return R.ok(0);
+ }
+
+ int disableCount = 0;
+ for (XpPoint point : customPoints) {
+ if (point == null || point.getEffectiveTimePeriod() == null || point.getEffectiveTimePeriod().isEmpty()) {
+ continue;
+ }
+ List<OperationStrategyIndex.TimeInterval> pointIntervals = OperationStrategyIndex.parsePointEffectiveTimePeriod(point.getEffectiveTimePeriod());
+ if (pointIntervals.isEmpty()) {
+ continue;
+ }
+
+ boolean shouldDisable = !newIndex.isPointScheduleWithin(pointIntervals);
+ if (shouldDisable) {
+ disableCount++;
+ }
+ }
+
+ List<XpStation> listByElectronicId = xpStationService.findListByElectronicId(dto.getId());
+ if(CollUtil.isNotEmpty(listByElectronicId)){
+
+ for (XpStation bean : listByElectronicId) {
+ if (StrUtil.isBlank(bean.getEffectiveTimePeriod())) {
+ continue;
+ }
+ List<OperationStrategyIndex.TimeInterval> pointIntervals = OperationStrategyIndex.parsePointEffectiveTimePeriod(bean.getEffectiveTimePeriod());
+ if (pointIntervals.isEmpty()) {
+ continue;
+ }
+
+ boolean shouldDisable = !newIndex.isPointScheduleWithin(pointIntervals);
+ if (shouldDisable) {
+ disableCount++;
+ }
+ }
+ }
+ return R.ok(disableCount);
}
/**
@@ -157,6 +360,12 @@
@ApiOperation(value = "复制电子围栏")
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
+ @AdminOperationLog(
+ businessType = AdminBusinessType.ELECTRONIC_FENCE_MANAGEMENT,
+ operationType = AdminOperationType.ADD,
+ contentExpression = "'复制电子围栏,id=' + #p0",
+ appendRequestData = false
+ )
public R<Long> copy(@RequestParam("id") Integer id) {
XpElectronicFence electronicFence = xpElectronicFenceService.getById(id);
if (electronicFence.getStatus()==2){
@@ -165,7 +374,7 @@
I18nKeyConstant.FENCE_STATUS_CANNOT_COPIED
);
}
- electronicFence.setElectronicName("副本_"+electronicFence.getElectronicName());
+ electronicFence.setElectronicName(buildCopyFenceName(electronicFence.getElectronicName()));
electronicFence.setId(null);
electronicFence.setStatus(0);
List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpElectronicFenceArea::getElectronicId, id)
@@ -193,6 +402,21 @@
return R.ok(electronicFence.getId());
}
+ private String buildCopyFenceName(String sourceName) {
+ Set<String> existingNames = new HashSet<>(xpElectronicFenceService.lambdaQuery()
+ .likeRight(XpElectronicFence::getElectronicName, sourceName)
+ .list()
+ .stream()
+ .map(XpElectronicFence::getElectronicName)
+ .collect(Collectors.toSet()));
+
+ String candidateName = sourceName + "_副本";
+ while (existingNames.contains(candidateName)) {
+ candidateName = candidateName + "_副本";
+ }
+ return candidateName;
+ }
+
/**
* 根据ID列表批量查询电子围栏
*
@@ -207,18 +431,13 @@
return R.ok(xpElectronicFenceService.listByIds(electronicIdSet));
}
- @ApiOperation(value = "获取已启用的电子围栏列表",tags = {"管理后台-在库车辆管理"})
+ @ApiOperation(value = "获取已启用的电子围栏列表",tags = {"管理后台-电子围栏"})
@GetMapping("/available/list")
R<List<XpElectronicFence>> getAvailableList(){
return R.ok(xpElectronicFenceService.getAvailableList());
}
- @ApiOperation(value = "获取不分页的电子围栏列表",tags = {"管理后台-在库车辆管理"})
- @GetMapping("/list")
- R<List<XpElectronicFence>> getList(){
- return R.ok(xpElectronicFenceService.getList());
- }
/**
@@ -243,4 +462,40 @@
XpElectronicFence xpElectronicFence = xpElectronicFenceService.getById(fenceId);
return R.ok(xpElectronicFence);
}
+
+ /**
+ * 校验围栏生效时间和失效时间的合法性:
+ * <ul>
+ * <li>生效类型不是立即生效(effectType != 1)时,生效时间不能早于当前时间</li>
+ * <li>失效类型不是永不失效(failureType != 1)时,失效时间不能早于当前时间</li>
+ * </ul>
+ *
+ * @param status
+ * @param effectType 生效类型(1=立即生效 2=定时生效)
+ * @param effectTime 生效时间
+ * @param failureType 失效类型(1=永不失效 2=定时失效)
+ * @param failureTime 失效时间
+ */
+ private void validateFenceTime(@NotNull Integer status, Integer effectType, LocalDateTime effectTime,
+ Integer failureType, LocalDateTime failureTime) {
+ LocalDateTime now = LocalDateTime.now();
+ // 发布/启用时,定时生效的生效时间不能早于当前时间
+ if (requiresScheduleValidation(status) && effectType != null && effectType != 1) {
+ if (effectTime == null || effectTime.isBefore(now)) {
+ throw new ServiceException("围栏生效时间不合法,定时生效时生效时间不能早于当前时间",
+ I18nKeyConstant.FENCE_EFFECT_TIME_INVALID);
+ }
+ }
+ // 发布/启用时,定时失效的失效时间不能早于当前时间
+ if (requiresScheduleValidation(status) && failureType != null && failureType != 1) {
+ if (failureTime == null || failureTime.isBefore(now)) {
+ throw new ServiceException("围栏失效时间不合法,定时失效时失效时间不能早于当前时间",
+ I18nKeyConstant.FENCE_FAILURE_TIME_INVALID);
+ }
+ }
+ }
+
+ private boolean requiresScheduleValidation(Integer status) {
+ return status != null && (status == 1 || status == 2);
+ }
}
--
Gitblit v1.7.1