yyy
2026-05-17 2c0a9a5194596d05b00aa773133582fa16deb6ea
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java
@@ -2,27 +2,33 @@
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.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.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.XpPointService;
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;
@@ -36,14 +42,21 @@
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.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>
@@ -73,6 +86,11 @@
    @Autowired
    private XpPointService xpPointService;
    @Resource
    private XpStationService xpStationService;
    @Resource
    private BootCommonService bootCommonService;
    /**
     * 查询省市管理
     */
@@ -81,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) {
@@ -92,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);
@@ -103,6 +135,12 @@
     */
    @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);
@@ -116,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) {
@@ -146,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);
    }
@@ -156,6 +260,12 @@
    @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);
@@ -221,6 +331,25 @@
                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);
    }
@@ -231,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){
@@ -239,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)
@@ -267,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列表批量查询电子围栏
     *
@@ -281,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());
    }
    /**
@@ -334,19 +479,23 @@
    private void validateFenceTime(@NotNull Integer status, Integer effectType, LocalDateTime effectTime,
                                   Integer failureType, LocalDateTime failureTime) {
        LocalDateTime now = LocalDateTime.now();
        // 定时生效时,生效时间不能早于当前时间
        if (null != status && status == 2 && effectType != null && effectType != 1) {
        // 发布/启用时,定时生效的生效时间不能早于当前时间
        if (requiresScheduleValidation(status) && effectType != null && effectType != 1) {
            if (effectTime == null || effectTime.isBefore(now)) {
                throw new ServiceException("围栏生效时间不合法,定时生效时生效时间不能早于当前时间",
                        I18nKeyConstant.FENCE_EFFECT_TIME_INVALID);
            }
        }
        // 定时失效时,失效时间不能早于当前时间
        if (null != status && status == 2 && failureType != null && failureType != 1) {
        // 发布/启用时,定时失效的失效时间不能早于当前时间
        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);
    }
}