package com.xiaopeng.operation.service.impl; 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.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.Map; import java.util.Set; import java.util.stream.Collectors; /** *

* 省市区三级联动 服务实现类 *

* * @author liu * @since 2026-02-03 */ @Service public class XpRegionServiceImpl extends ServiceImpl implements XpRegionService { @Resource private RedisService redisService; @Resource private XpOpenCityService openCityService; @Override public List> getXpRegion() { // 缓存数据 List cacheList = redisService.getCacheList(CacheConstants.XP_REGION); if (cacheList != null && !cacheList.isEmpty()) { try { return cacheList.stream() .filter(obj -> obj instanceof List) .map(obj -> { List list = (List) obj; return list.stream() .filter(item -> item instanceof XpRegion) .map(item -> (XpRegion) item) .collect(Collectors.toList()); }) .collect(Collectors.toList()); } catch (ClassCastException e) { // 缓存数据结构异常,重新加载 redisService.deleteObject(CacheConstants.XP_REGION); } } String[] initials = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; List tRegions = this.baseMapper.selectList(Wrappers.lambdaQuery(XpRegion.class).isNotNull(XpRegion::getInitial)); List> result = new ArrayList<>(); for (String initial : initials) { List init = tRegions.stream().filter(e -> e.getInitial().equals(initial) && !e.getCode().contains("0000")).collect(Collectors.toList()); result.add(init); } redisService.setCacheList(CacheConstants.XP_REGION, result); return result; } @Override public List getRegionCascader() { List 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 regionList = this.baseMapper.selectList(Wrappers.lambdaQuery(XpRegion.class)); if (regionList == null || regionList.isEmpty()) { return Collections.emptyList(); } List openCityList = openCityService.list(Wrappers.lambdaQuery(XpOpenCity.class) .eq(XpOpenCity::getStatus, 1)); Set openCityCodeSet = openCityList.stream() .map(XpOpenCity::getCityCode) .filter(StringUtils::isNotBlank) .collect(Collectors.toCollection(HashSet::new)); Set openDistrictCodeSet = openCityList.stream() .map(XpOpenCity::getDistrictCode) .filter(StringUtils::isNotBlank) .collect(Collectors.toCollection(HashSet::new)); Map> childrenMap = regionList.stream() .collect(Collectors.groupingBy(region -> region.getParentId() == null ? 0L : region.getParentId())); List provinceList = childrenMap.getOrDefault(0L, Collections.emptyList()).stream() .sorted(REGION_COMPARATOR) .collect(Collectors.toList()); List 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> childrenMap, Set openCityCodeSet, Set openDistrictCodeSet) { List children = childrenMap.getOrDefault(current.getId(), Collections.emptyList()).stream() .sorted(REGION_COMPARATOR) .collect(Collectors.toList()); List 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 openCityCodeSet, Set 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 REGION_COMPARATOR = Comparator .comparing((XpRegion region) -> StringUtils.defaultString(region.getInitial())) .thenComparing((XpRegion region) -> StringUtils.defaultString(region.getCode())); }