“mitao”
2026-02-13 0dd8ec867eb1ed398bc1b1e93538f55809df3679
feat:增加省市区级联接口
6个文件已修改
186 ■■■■■ 已修改文件
xp-robotaxi-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpRegionController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpRegionService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpRegionServiceImpl.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpRegionMapper.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -25,4 +25,5 @@
com.xiaopeng.api.factory.vehicleConvoy.VehicleControlClientFallbackFactory
com.xiaopeng.api.factory.automaticPilot.XPUClientFallbackFactory
com.xiaopeng.api.factory.automaticPilot.XpuVimClientFallbackFactory
com.xiaopeng.api.factory.maintenance.XpCheckLocationClientFallbackFactory
com.xiaopeng.api.factory.maintenance.XpCheckLocationClientFallbackFactory
com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory
xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/constant/CacheConstants.java
@@ -84,6 +84,11 @@
     */
    public static final String XP_REGION_CITY = "XP_REGION_CITY";
    /**
     * 省市区级联树
     */
    public static final String XP_REGION_CASCADER = "XP_REGION_CASCADER";
    /**
     * 节假日
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpRegionController.java
@@ -11,6 +11,7 @@
import com.xiaopeng.operation.query.getProvinceQuery;
import com.xiaopeng.operation.service.XpOpenCityService;
import com.xiaopeng.operation.service.XpRegionService;
import com.xiaopeng.operation.vo.RegionCascaderVO;
import com.xiaopeng.operation.vo.RegionDistrictSyncResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -100,6 +101,15 @@
    }
    /**
     * 管理后台-省市区三级级联查询
     */
    @ApiOperation(value = "管理后台-省市区三级级联查询", tags = {"管理后台-省市区管理"})
    @GetMapping("/api/v1/region/cascader")
    public R<List<RegionCascaderVO>> getRegionCascader() {
        return R.ok(regionService.getRegionCascader());
    }
    /**
     * 管理后台-同步区级数据
     */
    @ApiOperation(value = "管理后台-同步区级数据", tags = {"管理后台-省市区管理"})
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpRegionService.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiaopeng.api.model.operation.XpRegion;
import com.xiaopeng.operation.vo.RegionCascaderVO;
import com.xiaopeng.operation.vo.RegionDistrictSyncResultVO;
import java.util.List;
@@ -24,6 +25,12 @@
    List<List<XpRegion>> getXpRegion();
    /**
     * 管理后台-省市区三级级联查询
     * @return 级联树
     */
    List<RegionCascaderVO> getRegionCascader();
    /**
     * 从xp_region_all同步区级数据到xp_region
     * @return 同步结果
     */
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpRegionServiceImpl.java
@@ -3,19 +3,27 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.api.model.operation.XpOpenCity;
import com.xiaopeng.api.model.operation.XpRegion;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.redis.service.RedisService;
import com.xiaopeng.operation.mapper.XpRegionMapper;
import com.xiaopeng.api.model.operation.XpRegion;
import com.xiaopeng.operation.service.XpOpenCityService;
import com.xiaopeng.operation.service.XpRegionService;
import com.xiaopeng.operation.vo.RegionCascaderVO;
import com.xiaopeng.operation.vo.RegionDistrictSyncResultVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -31,6 +39,8 @@
    @Resource
    private RedisService redisService;
    @Resource
    private XpOpenCityService openCityService;
    @Override
    public List<List<XpRegion>> getXpRegion() {
@@ -69,10 +79,116 @@
    }
    @Override
    public List<RegionCascaderVO> getRegionCascader() {
        List<Object> cacheList = redisService.getCacheList(CacheConstants.XP_REGION_CASCADER);
        if (cacheList != null && !cacheList.isEmpty()) {
            try {
                return cacheList.stream()
                        .filter(item -> item instanceof RegionCascaderVO)
                        .map(item -> (RegionCascaderVO) item)
                        .collect(Collectors.toList());
            } catch (ClassCastException e) {
                redisService.deleteObject(CacheConstants.XP_REGION_CASCADER);
            }
        }
        List<XpRegion> regionList = this.baseMapper.selectList(Wrappers.lambdaQuery(XpRegion.class));
        if (regionList == null || regionList.isEmpty()) {
            return Collections.emptyList();
        }
        List<XpOpenCity> openCityList = openCityService.list(Wrappers.lambdaQuery(XpOpenCity.class)
                .eq(XpOpenCity::getStatus, 1));
        Set<String> openCityCodeSet = openCityList.stream()
                .map(XpOpenCity::getCityCode)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toCollection(HashSet::new));
        Set<String> openDistrictCodeSet = openCityList.stream()
                .map(XpOpenCity::getDistrictCode)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toCollection(HashSet::new));
        Map<Long, List<XpRegion>> childrenMap = regionList.stream()
                .collect(Collectors.groupingBy(region -> region.getParentId() == null ? 0L : region.getParentId()));
        List<XpRegion> provinceList = childrenMap.getOrDefault(0L, Collections.emptyList()).stream()
                .sorted(REGION_COMPARATOR)
                .collect(Collectors.toList());
        List<RegionCascaderVO> result = provinceList.stream()
                .map(region -> buildRegionNode(region, childrenMap, openCityCodeSet, openDistrictCodeSet))
                .collect(Collectors.toList());
        redisService.setCacheList(CacheConstants.XP_REGION_CASCADER, result);
        return result;
    }
    private RegionCascaderVO buildRegionNode(XpRegion current,
                                             Map<Long, List<XpRegion>> childrenMap,
                                             Set<String> openCityCodeSet,
                                             Set<String> openDistrictCodeSet) {
        List<XpRegion> children = childrenMap.getOrDefault(current.getId(), Collections.emptyList()).stream()
                .sorted(REGION_COMPARATOR)
                .collect(Collectors.toList());
        List<RegionCascaderVO> childNodes = children.stream()
                .map(child -> buildRegionNode(child, childrenMap, openCityCodeSet, openDistrictCodeSet))
                .collect(Collectors.toList());
        Integer level = resolveRegionLevel(current.getCode());
        Integer selfOpen = resolveSelfOpen(level, current.getCode(), openCityCodeSet, openDistrictCodeSet);
        boolean hasOpenChild = childNodes.stream().anyMatch(node -> Integer.valueOf(1).equals(node.getIsOpen()));
        RegionCascaderVO node = new RegionCascaderVO();
        node.setCode(current.getCode());
        node.setName(current.getName());
        node.setLeaf(Integer.valueOf(3).equals(level));
        node.setChildren(childNodes);
        node.setIsOpen(selfOpen == 1 || hasOpenChild ? 1 : 0);
        return node;
    }
    private Integer resolveRegionLevel(String code) {
        if (StringUtils.isBlank(code) || code.length() < 4) {
            return 3;
        }
        if (code.endsWith("0000")) {
            return 1;
        }
        if (code.endsWith("00")) {
            return 2;
        }
        return 3;
    }
    private Integer resolveSelfOpen(Integer level,
                                    String code,
                                    Set<String> openCityCodeSet,
                                    Set<String> openDistrictCodeSet) {
        if (StringUtils.isBlank(code)) {
            return 0;
        }
        if (Integer.valueOf(2).equals(level)) {
            return openCityCodeSet.contains(code) ? 1 : 0;
        }
        if (Integer.valueOf(3).equals(level)) {
            return openDistrictCodeSet.contains(code) ? 1 : 0;
        }
        return 0;
    }
    private static final Comparator<XpRegion> REGION_COMPARATOR = Comparator
            .comparing((XpRegion region) -> StringUtils.defaultString(region.getInitial()))
            .thenComparing((XpRegion region) -> StringUtils.defaultString(region.getCode()));
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RegionDistrictSyncResultVO syncDistrictFromRegionAll() {
        Long cityCount = Objects.requireNonNullElse(this.baseMapper.countCityForDistrictSync(), 0L);
        Long candidateDistrictCount = Objects.requireNonNullElse(this.baseMapper.countCandidateDistrictForSync(), 0L);
        Long cityCount = this.baseMapper.countCityForDistrictSync();
        if (cityCount == null) {
            cityCount = 0L;
        }
        Long candidateDistrictCount = this.baseMapper.countCandidateDistrictForSync();
        if (candidateDistrictCount == null) {
            candidateDistrictCount = 0L;
        }
        List<XpRegion> missingDistrictList = this.baseMapper.selectMissingDistrictFromRegionAll();
        long insertedCount = 0L;
@@ -83,6 +199,7 @@
        redisService.deleteObject(CacheConstants.XP_REGION);
        redisService.deleteObject(CacheConstants.XP_REGION_CITY);
        redisService.deleteObject(CacheConstants.XP_REGION_CASCADER);
        RegionDistrictSyncResultVO result = new RegionDistrictSyncResultVO();
        result.setCityCount(cityCount);
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpRegionMapper.xml
@@ -2,6 +2,42 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiaopeng.operation.mapper.XpRegionMapper">
    <select id="selectMissingDistrictFromRegionAll" resultType="com.xiaopeng.api.model.operation.XpRegion">
        select
            district.name as name,
            district.code as code,
            district.citycode as cityCode,
            city.id as parentId,
            district.english as english,
            city.initial as initial
        from xp_region city
                 join xp_region_all all_city on all_city.code = city.code
                 join xp_region_all district on district.parent_id = all_city.id
                 left join xp_region existing on existing.code = district.code
        where city.parentId != 0
          and city.code like '%00'
          and city.code not like '%0000'
          and existing.id is null
        order by city.code, district.code
    </select>
    <select id="countCityForDistrictSync" resultType="java.lang.Long">
        select count(1)
        from xp_region city
                 join xp_region_all all_city on all_city.code = city.code
        where city.parentId != 0
          and city.code like '%00'
          and city.code not like '%0000'
    </select>
    <select id="countCandidateDistrictForSync" resultType="java.lang.Long">
        select count(1)
        from xp_region city
                 join xp_region_all all_city on all_city.code = city.code
                 join xp_region_all district on district.parent_id = all_city.id
        where city.parentId != 0
          and city.code like '%00'
          and city.code not like '%0000'
    </select>
</mapper>